From d6d1b5bbdca1461392e6eb8f57dabf9d88038250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 3 Mar 2025 12:57:27 +0100 Subject: [PATCH] Add mechanism to run any closure on the main thread --- Common/System/System.h | 3 +++ UI/NativeApp.cpp | 42 ++++++++++++++++++++++++++------------ Windows/MainWindowMenu.cpp | 4 +--- headless/Headless.cpp | 1 + libretro/libretro.cpp | 1 + unittest/UnitTest.cpp | 5 ++--- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Common/System/System.h b/Common/System/System.h index 6c945be000..700ea141a4 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -100,6 +100,9 @@ enum class SystemRequestType { RUN_CALLBACK_IN_WNDPROC, }; +// Run a closure on the main thread. Used to safely implement UI that runs on another thread. +void System_RunOnMainThread(std::function func); + // Implementations are supposed to process the request, and post the response to the g_RequestManager (see Message.h). // This is not to be used directly by applications, instead use the g_RequestManager to make the requests. // This can return false if it's known that the platform doesn't support the request, the app is supposed to handle diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 0be097cd21..e3058fbb7c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -177,7 +177,7 @@ struct PendingMessage { std::string value; }; -static std::mutex pendingMutex; +static std::mutex g_pendingMutex; static std::vector pendingMessages; static Draw::DrawContext *g_draw; static Draw::Pipeline *colorPipeline; @@ -185,6 +185,7 @@ static Draw::Pipeline *texColorPipeline; static UIContext *uiContext; static int g_restartGraphics; static bool g_windowHidden = false; +std::vector> g_pendingClosures; #ifdef _WIN32 WindowsAudioBackend *winAudioBackend; @@ -345,6 +346,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch setlocale( LC_ALL, "C" ); std::string user_data_path = savegame_dir; pendingMessages.clear(); + g_pendingClosures.clear(); g_requestManager.Clear(); // external_dir has all kinds of meanings depending on platform. @@ -1047,19 +1049,28 @@ void NativeFrame(GraphicsContext *graphicsContext) { g_screenManager->update(); // Do this after g_screenManager.update() so we can receive setting changes before rendering. - std::vector toProcess; { - std::lock_guard lock(pendingMutex); - toProcess = std::move(pendingMessages); - pendingMessages.clear(); - } - - for (const auto &item : toProcess) { - if (HandleGlobalMessage(item.message, item.value)) { - // TODO: Add a to-string thingy. - INFO_LOG(Log::System, "Handled global message: %d / %s", (int)item.message, item.value.c_str()); + std::vector toProcess; + std::vector> toRun; + { + std::lock_guard lock(g_pendingMutex); + toProcess = std::move(pendingMessages); + toRun = std::move(g_pendingClosures); + pendingMessages.clear(); + g_pendingClosures.clear(); + } + + for (auto &item : toRun) { + item(); + } + + for (const auto &item : toProcess) { + if (HandleGlobalMessage(item.message, item.value)) { + // TODO: Add a to-string thingy. + INFO_LOG(Log::System, "Handled global message: %d / %s", (int)item.message, item.value.c_str()); + } + g_screenManager->sendMessage(item.message, item.value.c_str()); } - g_screenManager->sendMessage(item.message, item.value.c_str()); } g_requestManager.ProcessRequests(); @@ -1404,13 +1415,18 @@ void NativeAccelerometer(float tiltX, float tiltY, float tiltZ) { } void System_PostUIMessage(UIMessage message, const std::string &value) { - std::lock_guard lock(pendingMutex); + std::lock_guard lock(g_pendingMutex); PendingMessage pendingMessage; pendingMessage.message = message; pendingMessage.value = value; pendingMessages.push_back(pendingMessage); } +void System_RunOnMainThread(std::function func) { + std::lock_guard lock(g_pendingMutex); + g_pendingClosures.push_back(std::move(func)); +} + void NativeResized() { // NativeResized can come from any thread so we just set a flag, then process it later. VERBOSE_LOG(Log::G3D, "NativeResized - setting flag"); diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 7d931a6093..ab57497406 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -497,9 +497,7 @@ namespace MainWindow { break; case ID_EMULATION_PAUSE: - if (!NetworkWarnUserIfOnlineAndCantSpeed()) { - System_PostUIMessage(UIMessage::REQUEST_GAME_PAUSE); - } + System_PostUIMessage(UIMessage::REQUEST_GAME_PAUSE); break; case ID_EMULATION_STOP: diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 53f41c6516..b818eebd86 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -93,6 +93,7 @@ bool System_GetPropertyBool(SystemProperty prop) { } void System_Notify(SystemNotification notification) {} void System_PostUIMessage(UIMessage message, const std::string ¶m) {} +void System_RunOnMainThread(std::function) {} bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { switch (type) { case SystemRequestType::SEND_DEBUG_OUTPUT: diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index f31af7beeb..523c5f4d3b 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1913,6 +1913,7 @@ void System_Notify(SystemNotification notification) { } bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { return false; } void System_PostUIMessage(UIMessage message, const std::string ¶m) {} +void System_RunOnMainThread(std::function) {} void NativeFrame(GraphicsContext *graphicsContext) {} void NativeResized() {} diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 733bb10884..4c45c365a1 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -112,6 +112,7 @@ bool System_GetPropertyBool(SystemProperty prop) { } void System_Notify(SystemNotification notification) {} void System_PostUIMessage(UIMessage message, const std::string ¶m) {} +void System_RunOnMainThread(std::function) {} void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples, float volume) {} @@ -119,9 +120,7 @@ void System_AudioPushSamples(const s32 *audio, int numSamples, float volume) {} // TODO: To avoid having to define these here, these should probably be turned into system "requests". // To clear the secret entirely, just save an empty string. bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; } -std::string NativeLoadSecret(std::string_view nameOfSecret) { - return ""; -} +std::string NativeLoadSecret(std::string_view nameOfSecret) { return ""; } #if PPSSPP_PLATFORM(ANDROID) JNIEnv *getEnv() {