From 049a7a04de7d51e8905920996a75196ce6fea101 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 e7216ee408b50..157f0180061ab 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 aca6fafb7319c..ed4e684cff586 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;
+
+ window = SDL_CreateWindow("test", 0, 0, SDL_WINDOW_HIDDEN);
+ SDLTest_AssertCheck(window != NULL, "Create window, expected non-NULL, got %p", window);
- thread = SDL_CreateThread(IncrementCounterThread, NULL, &counter);
+ /* 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 */