From acc7fa67549cd8f29a5b0f70eb9c8187df97f504 Mon Sep 17 00:00:00 2001 From: Ken Sanislo Date: Sun, 26 Jul 2026 22:47:18 +0000 Subject: [PATCH] Invoke resource manager callbacks outside the callback mutex --- ResourceManager.cpp | 20 ++++++++++++++++---- qt/OpenRGBDialog/OpenRGBDialog.cpp | 8 ++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ResourceManager.cpp b/ResourceManager.cpp index 0366ce14f..83fa7aaaa 100644 --- a/ResourceManager.cpp +++ b/ResourceManager.cpp @@ -811,15 +811,27 @@ void ResourceManager::SignalResourceManagerUpdate(unsigned int update_reason) server->SignalResourceManagerUpdate(update_reason); } + /*-----------------------------------------------------*\ + | Snapshot the callback list, then invoke unlocked. A | + | blocking callback must not hold the mutex: the GUI's | + | blocking device list callback waits forever once the | + | event loop has exited at shutdown, and a held mutex | + | then deadlocks the unregister in the dialog | + | destructor. A callback may still fire once after its | + | unregister returns. | + \*-----------------------------------------------------*/ ResourceManagerCallbackMutex.lock(); - for(std::size_t callback_idx = 0; callback_idx < ResourceManagerCallbacks.size(); callback_idx++) - { - ResourceManagerCallbacks[callback_idx](ResourceManagerCallbackArgs[callback_idx], update_reason); - } + std::vector callbacks = ResourceManagerCallbacks; + std::vector callback_args = ResourceManagerCallbackArgs; ResourceManagerCallbackMutex.unlock(); + for(std::size_t callback_idx = 0; callback_idx < callbacks.size(); callback_idx++) + { + callbacks[callback_idx](callback_args[callback_idx], update_reason); + } + LOG_TRACE("[%s] ResourceManager update signalled: %d", RESOURCEMANAGER, update_reason); } diff --git a/qt/OpenRGBDialog/OpenRGBDialog.cpp b/qt/OpenRGBDialog/OpenRGBDialog.cpp index ab19e8200..910dd8bb0 100644 --- a/qt/OpenRGBDialog/OpenRGBDialog.cpp +++ b/qt/OpenRGBDialog/OpenRGBDialog.cpp @@ -577,6 +577,14 @@ void OpenRGBDialog::closeEvent(QCloseEvent *event) } else { + /*-------------------------------------------------*\ + | Stop receiving resource manager callbacks before | + | the event loop dies: plugin teardown signals from | + | worker threads would post blocking calls no loop | + | will ever service. | + \*-------------------------------------------------*/ + ResourceManager::get()->UnregisterResourceManagerCallback(OpenRGBDialogResourceManagerCallback, this); + plugin_manager->UnloadPlugins(); if(ResourceManager::get()->GetProfileManager()->LoadAutoProfileExit())