File: deadLockPlugin.cpp

package info (click to toggle)
mumble 1.5.735-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 90,008 kB
  • sloc: cpp: 556,921; ansic: 81,662; python: 3,606; sh: 659; makefile: 506; asm: 371; cs: 306; sql: 228; javascript: 143; perl: 80; xml: 13
file content (172 lines) | stat: -rw-r--r-- 6,131 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright 2021-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.

// Include the definitions of the plugin functions
// Not that this will also include ../PluginComponents.h
#include "../MumblePlugin.h"

#include <condition_variable>
#include <cstring>
#include <iostream>
#include <mutex>
#include <thread>

MumbleAPI mumAPI;
mumble_connection_t activeConnection;
mumble_plugin_id_t ownID;
std::mutex dummyMutex;
std::condition_variable waiter;
bool dummyRan                = false;
bool deadlockPreventionWorks = false;

//////////////////////////////////////////////////////////////
//////////////////// OBLIGATORY FUNCTIONS ////////////////////
//////////////////////////////////////////////////////////////

mumble_error_t mumble_init(uint32_t id) {
	ownID = id;
	mumAPI.log(ownID, "Intitialized");

	// MUMBLE_STATUS_OK is a macro set to the appropriate status flag (ErrorCode)
	// If you need to return any other status have a look at the ErrorCode enum
	// inside PluginComponents.h and use one of its values
	return MUMBLE_STATUS_OK;
}

void mumble_shutdown() {
	mumAPI.log(ownID, "Shutdown");
}

MumbleStringWrapper mumble_getName() {
	static const char *name = "DeadLockPlugin";

	MumbleStringWrapper wrapper;
	wrapper.data = name;
	wrapper.size = strlen(name);
	// It's a static String and therefore doesn't need releasing
	wrapper.needsReleasing = false;

	return wrapper;
}

mumble_version_t mumble_getAPIVersion() {
	// MUMBLE_PLUGIN_API_VERSION will always contain the API version of the used header file (the one used to build
	// this plugin against). Thus you should always return that here in order to no have to worry about it.
	return MUMBLE_PLUGIN_API_VERSION;
}

void mumble_registerAPIFunctions(void *api) {
	// In this function the plugin is presented with a struct of function pointers that can be used
	// to interact with Mumble. Thus you should store it somewhere safe for later usage.

	// The pointer has to be cast to the respective API struct. You always have to cast to the same API version
	// as this plugin itself is using. Thus if this plugin is compiled using the API version 1.0.x (where x is an
	// arbitrary version) the pointer has to be cast to MumbleAPI_v_1_0_x (where x is a literal "x"). Furthermore the
	// struct HAS TO BE COPIED!!! Storing the pointer is not an option as it will become invalid quickly!

	// **If** you are using the same API version that is specified in the included header file (as you should), you
	// can simply use the MUMBLE_API_CAST to cast the pointer to the correct type and automatically dereferencing it.
	mumAPI = MUMBLE_API_CAST(api);
}

void mumble_releaseResource(const void *pointer) {
	std::cerr << "[ERROR]: Unexpected call to mumble_releaseResources" << std::endl;
	std::terminate();

	// Mark as unused
	(void) pointer;
}


//////////////////////////////////////////////////////////////
///////////////////// OPTIONAL FUNCTIONS /////////////////////
//////////////////////////////////////////////////////////////
// The implementation of below functions is optional. If you don't need them, don't include them in your
// plugin

mumble_version_t mumble_getVersion() {
	// Mumble uses semantic versioning (see https://semver.org/)
	// { major, minor, patch }
	return { 1, 0, 0 };
}

MumbleStringWrapper mumble_getAuthor() {
	static const char *author = "MumbleDevelopers";

	MumbleStringWrapper wrapper;
	wrapper.data = author;
	wrapper.size = strlen(author);
	// It's a static String and therefore doesn't need releasing
	wrapper.needsReleasing = false;

	return wrapper;
}

MumbleStringWrapper mumble_getDescription() {
	static const char *description =
		"This plugin intentionally produces a deadlock (if Mumble doesn't prevent it properly). "
		"It shouldn't be included in the release build of Mumble.";

	MumbleStringWrapper wrapper;
	wrapper.data = description;
	wrapper.size = strlen(description);
	// It's a static String and therefore doesn't need releasing
	wrapper.needsReleasing = false;

	return wrapper;
}

uint32_t mumble_getFeatures() {
	// Tells Mumble whether this plugin delivers some known common functionality. See the PluginFeature enum in
	// PluginComponents.h for what is available.
	// If you want your plugin to deliver positional data, you'll want to return FEATURE_POSITIONAL
	return MUMBLE_FEATURE_NONE;
}

uint32_t mumble_deactivateFeatures(uint32_t features) {
	return features;
}

void dummyFunction() {
	std::lock_guard< std::mutex > guard(dummyMutex);

	// The client has finished synchronizing with the server. Thus we can now obtain a list of all users on this server
	mumble_connection_t connection;
	mumble_error_t error = mumAPI.getActiveServerConnection(ownID, &connection);
	if (error == MUMBLE_EC_API_REQUEST_TIMEOUT) {
		deadlockPreventionWorks = true;
	} else {
		std::cerr << "[ERROR]: Deadlocked API request returned " << error << " (\"" << mumble_errorMessage(error)
				  << "\")" << std::endl;

		deadlockPreventionWorks = false;
	}

	dummyRan = true;
	waiter.notify_all();
}

void mumble_onServerSynchronized(mumble_connection_t) {
	mumAPI.log(ownID, "Starting deadlock attempt...");

	// We enter this function, which is called from Mumble's main thread
	// We spawn a separate thread and make sure that we block this function until the separate thread has finished. In
	// that separate thread, we are calling an API function, which Mumble needs to execute in the main thread (for
	// synchronization purposes). However, since we are blocking the main thread here, we produce a deadlock. Only if
	// Mumble's deadlock prevention works as expected, is a freeze prevented.
	std::unique_lock< std::mutex > lock(dummyMutex);

	std::thread myThread(dummyFunction);

	waiter.wait(lock, []() { return dummyRan; });

	myThread.join();

	if (deadlockPreventionWorks) {
		mumAPI.log(ownID, "Deadlock prevented successfully");
	} else {
		mumAPI.log(ownID, "Deadlock prevented but not in the expected way");
	}
}