Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Adam <dev@robert-adam.de>2021-11-09 12:24:24 +0300
committerGitHub <noreply@github.com>2021-11-09 12:24:24 +0300
commitc032df80362ad87f9eb716bdd0da360a392ce6cc (patch)
tree58266093ab4b515c8dea98f571b0f31bfad90907
parent77e9620b938f96f941aed09d7ae97bc05ed9988d (diff)
parentfea949767a39b18b15499c0b9161718d9071463a (diff)
Merge PR #5299: TEST(client): Add plugin to test deadlock prevention
This plugin tests the built-in deadlock prevention of the Mumble plugin API.
-rw-r--r--plugins/CMakeLists.txt1
-rw-r--r--plugins/deadLockPlugin/CMakeLists.txt6
-rw-r--r--plugins/deadLockPlugin/deadLockPlugin.cpp173
3 files changed, 180 insertions, 0 deletions
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 95739f0ed..b20667d82 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -21,6 +21,7 @@ if(${CMAKE_BUILD_TYPE} MATCHES Debug)
message("Including TestPlugin in debug mode")
list(APPEND AVAILABLE_PLUGINS
"testPlugin"
+ "deadLockPlugin"
)
endif()
diff --git a/plugins/deadLockPlugin/CMakeLists.txt b/plugins/deadLockPlugin/CMakeLists.txt
new file mode 100644
index 000000000..8e28af5f0
--- /dev/null
+++ b/plugins/deadLockPlugin/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright 2021 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>.
+
+add_library(deadLockPlugin SHARED "deadLockPlugin.cpp")
diff --git a/plugins/deadLockPlugin/deadLockPlugin.cpp b/plugins/deadLockPlugin/deadLockPlugin.cpp
new file mode 100644
index 000000000..35ef820a9
--- /dev/null
+++ b/plugins/deadLockPlugin/deadLockPlugin.cpp
@@ -0,0 +1,173 @@
+// Copyright 2021 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 "../MumbleAPI_v_1_0_x.h"
+#include "../MumblePlugin_v_1_0_x.h"
+
+#include <condition_variable>
+#include <cstring>
+#include <iostream>
+#include <mutex>
+#include <thread>
+
+MumbleAPI_v_1_0_x 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");
+ }
+}