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");
}
}
|