Browse Source

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
Sam Lantinga 1 month ago
parent
commit
049a7a04de
2 changed files with 60 additions and 12 deletions
  1. 3 0
      src/events/SDL_events.c
  2. 57 12
      test/testautomation_events.c

+ 3 - 0
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;

+ 57 - 12
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 */