From 049a7a04de7d51e8905920996a75196ce6fea101 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 25 Feb 2025 12:23:32 -0800 Subject: [PATCH] Wake the main thread for main function dispatch Also added a test case to catch the main thread waiting indefinitely when a function is pending. Fixes https://github.com/libsdl-org/SDL/issues/12390 --- src/events/SDL_events.c | 3 ++ test/testautomation_events.c | 69 +++++++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index e7216ee408..157f018006 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -1371,6 +1371,9 @@ bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool w } SDL_UnlockMutex(SDL_main_callbacks_lock); + // If the main thread is waiting for events, wake it up + SDL_SendWakeupEvent(); + if (!wait_complete) { // Queued for execution, wait not requested return true; diff --git a/test/testautomation_events.c b/test/testautomation_events.c index aca6fafb73..ed4e684cff 100644 --- a/test/testautomation_events.c +++ b/test/testautomation_events.c @@ -211,48 +211,93 @@ static int SDLCALL events_addDelEventWatchWithUserdata(void *arg) * */ +typedef struct IncrementCounterData_t +{ + Uint32 delay; + int counter; +} IncrementCounterData_t; + static void SDLCALL IncrementCounter(void *userdata) { - int *value = (int *)userdata; - *value = *value + 1; + IncrementCounterData_t *data = (IncrementCounterData_t *)userdata; + ++data->counter; } #ifndef SDL_PLATFORM_EMSCRIPTEN /* Emscripten doesn't have threads */ static int SDLCALL IncrementCounterThread(void *userdata) { + IncrementCounterData_t *data = (IncrementCounterData_t *)userdata; + SDL_Event event; + SDL_assert(!SDL_IsMainThread()); - SDL_RunOnMainThread(IncrementCounter, userdata, false); - SDL_RunOnMainThread(IncrementCounter, userdata, true); + + if (data->delay > 0) { + SDL_Delay(data->delay); + } + + if (!SDL_RunOnMainThread(IncrementCounter, userdata, false)) { + SDLTest_LogError("Couldn't run IncrementCounter asynchronously on main thread: %s", SDL_GetError()); + } + if (!SDL_RunOnMainThread(IncrementCounter, userdata, true)) { + SDLTest_LogError("Couldn't run IncrementCounter synchronously on main thread: %s", SDL_GetError()); + } + + /* Send an event to unblock the main thread, which is waiting in SDL_WaitEvent() */ + event.type = SDL_EVENT_USER; + SDL_PushEvent(&event); + return 0; } #endif /* !SDL_PLATFORM_EMSCRIPTEN */ static int SDLCALL events_mainThreadCallbacks(void *arg) { - int counter = 0; + IncrementCounterData_t data = { 0, 0 }; /* Make sure we're on the main thread */ SDLTest_AssertCheck(SDL_IsMainThread(), "Verify we're on the main thread"); - SDL_RunOnMainThread(IncrementCounter, &counter, true); - SDLTest_AssertCheck(counter == 1, "Incremented counter on main thread, expected 1, got %d", counter); + SDL_RunOnMainThread(IncrementCounter, &data, true); + SDLTest_AssertCheck(data.counter == 1, "Incremented counter on main thread, expected 1, got %d", data.counter); #ifndef SDL_PLATFORM_EMSCRIPTEN /* Emscripten doesn't have threads */ { + SDL_Window *window; SDL_Thread *thread; + SDL_Event event; - thread = SDL_CreateThread(IncrementCounterThread, NULL, &counter); + window = SDL_CreateWindow("test", 0, 0, SDL_WINDOW_HIDDEN); + SDLTest_AssertCheck(window != NULL, "Create window, expected non-NULL, got %p", window); + + /* Flush any pending events */ + SDL_PumpEvents(); + SDL_FlushEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST); + + /* Increment the counter on a thread, waiting for both calls to be queued */ + thread = SDL_CreateThread(IncrementCounterThread, NULL, &data); SDLTest_AssertCheck(thread != NULL, "Create counter thread"); /* Wait for both increment calls to be queued up */ SDL_Delay(100); /* Run the main callbacks */ - while (counter < 3) { - SDL_PumpEvents(); - } + SDL_WaitEvent(&event); + SDLTest_AssertCheck(event.type == SDL_EVENT_USER, "Expected user event (0x%.4x), got 0x%.4x", SDL_EVENT_USER, (int)event.type); SDL_WaitThread(thread, NULL); - SDLTest_AssertCheck(counter == 3, "Incremented counter on main thread, expected 3, got %d", counter); + SDLTest_AssertCheck(data.counter == 3, "Incremented counter on main thread, expected 3, got %d", data.counter); + + /* Try again, but this time delay the calls until we've started waiting for events */ + data.delay = 100; + thread = SDL_CreateThread(IncrementCounterThread, NULL, &data); + SDLTest_AssertCheck(thread != NULL, "Create counter thread"); + + /* Run the main callbacks */ + SDL_WaitEvent(&event); + SDLTest_AssertCheck(event.type == SDL_EVENT_USER, "Expected user event (0x%.4x), got 0x%.4x", SDL_EVENT_USER, (int)event.type); + SDL_WaitThread(thread, NULL); + SDLTest_AssertCheck(data.counter == 5, "Incremented counter on main thread, expected 5, got %d", data.counter); + + SDL_DestroyWindow(window); } #endif /* !SDL_PLATFORM_EMSCRIPTEN */