Browse Source

Fixed rare crash trying to interrupt SDL_WaitEvent()

Fixes https://github.com/libsdl-org/SDL/issues/12797

(cherry picked from commit 992e4c59bdf03dbec8b1689332ee697623cf6ad2)
(cherry picked from commit 33eb167da8487e543cba4204de856da65fa9ca51)
Sam Lantinga 3 months ago
parent
commit
4177e06c09

+ 8 - 25
src/events/SDL_events.c

@@ -738,21 +738,17 @@ static void SDL_CutEvent(SDL_EventEntry *entry)
 
 static int SDL_SendWakeupEvent(void)
 {
+    SDL_Window *wakeup_window;
     SDL_VideoDevice *_this = SDL_GetVideoDevice();
     if (_this == NULL || !_this->SendWakeupEvent) {
         return 0;
     }
 
-    SDL_LockMutex(_this->wakeup_lock);
-    {
-        if (_this->wakeup_window) {
-            _this->SendWakeupEvent(_this, _this->wakeup_window);
-
-            /* No more wakeup events needed until we enter a new wait */
-            _this->wakeup_window = NULL;
-        }
+    /* We only want to do this once while waiting for an event, so set it to NULL atomically here */
+    wakeup_window = (SDL_Window *)SDL_AtomicSetPtr(&_this->wakeup_window, NULL);
+    if (wakeup_window) {
+        _this->SendWakeupEvent(_this, wakeup_window);
     }
-    SDL_UnlockMutex(_this->wakeup_lock);
 
     return 0;
 }
@@ -1009,18 +1005,7 @@ static int SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Eve
         int status;
         SDL_PumpEventsInternal(SDL_TRUE);
 
-        SDL_LockMutex(_this->wakeup_lock);
-        {
-            status = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
-            /* If status == 0 we are going to block so wakeup will be needed. */
-            if (status == 0) {
-                _this->wakeup_window = wakeup_window;
-            } else {
-                _this->wakeup_window = NULL;
-            }
-        }
-        SDL_UnlockMutex(_this->wakeup_lock);
-
+        status = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
         if (status < 0) {
             /* Got an error: return */
             break;
@@ -1033,8 +1018,6 @@ static int SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Eve
         if (timeout > 0) {
             Uint32 elapsed = SDL_GetTicks() - start;
             if (elapsed >= (Uint32)timeout) {
-                /* Set wakeup_window to NULL without holding the lock. */
-                _this->wakeup_window = NULL;
                 return 0;
             }
             loop_timeout = (int)((Uint32)timeout - elapsed);
@@ -1049,9 +1032,9 @@ static int SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Eve
             }
         }
 
+        SDL_AtomicSetPtr(&_this->wakeup_window, wakeup_window);
         status = _this->WaitEventTimeout(_this, loop_timeout);
-        /* Set wakeup_window to NULL without holding the lock. */
-        _this->wakeup_window = NULL;
+        SDL_AtomicSetPtr(&_this->wakeup_window, NULL);
         if (status == 0 && poll_interval != SDL_MAX_SINT16 && loop_timeout == poll_interval) {
             /* We may have woken up to poll. Try again */
             continue;

+ 1 - 2
src/video/SDL_sysvideo.h

@@ -355,8 +355,7 @@ struct SDL_VideoDevice
     SDL_bool checked_texture_framebuffer;
     SDL_bool is_dummy;
     SDL_bool suspend_screensaver;
-    SDL_Window *wakeup_window;
-    SDL_mutex *wakeup_lock; /* Initialized only if WaitEventTimeout/SendWakeupEvent are supported */
+    void *wakeup_window;
     int num_displays;
     SDL_VideoDisplay *displays;
     SDL_Window *windows;

+ 1 - 3
src/video/SDL_video.c

@@ -3368,9 +3368,7 @@ void SDL_DestroyWindow(SDL_Window *window)
         _this->current_glwin = NULL;
     }
 
-    if (_this->wakeup_window == window) {
-        _this->wakeup_window = NULL;
-    }
+    SDL_AtomicCASPtr(&_this->wakeup_window, window, NULL);
 
     /* Now invalidate magic */
     window->magic = NULL;

+ 0 - 4
src/video/cocoa/SDL_cocoavideo.m

@@ -48,9 +48,6 @@ static void Cocoa_VideoQuit(_THIS);
 static void Cocoa_DeleteDevice(SDL_VideoDevice * device)
 { @autoreleasepool
 {
-    if (device->wakeup_lock) {
-        SDL_DestroyMutex(device->wakeup_lock);
-    }
     CFBridgingRelease(device->driverdata);
     SDL_free(device);
 }}
@@ -76,7 +73,6 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
         return NULL;
     }
     device->driverdata = (void *)CFBridgingRetain(data);
-    device->wakeup_lock = SDL_CreateMutex();
 
     /* Set the function pointers */
     device->VideoInit = Cocoa_VideoInit;

+ 0 - 4
src/video/wayland/SDL_waylandvideo.c

@@ -162,9 +162,6 @@ static void Wayland_DeleteDevice(SDL_VideoDevice *device)
         WAYLAND_wl_display_flush(data->display);
         WAYLAND_wl_display_disconnect(data->display);
     }
-    if (device->wakeup_lock) {
-        SDL_DestroyMutex(device->wakeup_lock);
-    }
     SDL_free(data);
     SDL_free(device);
     SDL_WAYLAND_UnloadSymbols();
@@ -233,7 +230,6 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
     }
 
     device->driverdata = data;
-    device->wakeup_lock = SDL_CreateMutex();
 
     /* Set the function pointers */
     device->VideoInit = Wayland_VideoInit;

+ 0 - 4
src/video/windows/SDL_windowsvideo.c

@@ -93,9 +93,6 @@ static void WIN_DeleteDevice(SDL_VideoDevice *device)
         SDL_UnloadObject(data->shcoreDLL);
     }
 #endif
-    if (device->wakeup_lock) {
-        SDL_DestroyMutex(device->wakeup_lock);
-    }
     SDL_free(device->driverdata);
     SDL_free(device);
 }
@@ -120,7 +117,6 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
         return NULL;
     }
     device->driverdata = data;
-    device->wakeup_lock = SDL_CreateMutex();
 
 #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
     data->userDLL = SDL_LoadObject("USER32.DLL");

+ 0 - 5
src/video/x11/SDL_x11video.c

@@ -108,9 +108,6 @@ static void X11_DeleteDevice(SDL_VideoDevice *device)
         X11_XCloseDisplay(data->request_display);
     }
     SDL_free(data->windowlist);
-    if (device->wakeup_lock) {
-        SDL_DestroyMutex(device->wakeup_lock);
-    }
     SDL_free(device->driverdata);
     SDL_free(device);
 
@@ -204,8 +201,6 @@ static SDL_VideoDevice *X11_CreateDevice(void)
         return NULL;
     }
 
-    device->wakeup_lock = SDL_CreateMutex();
-
 #ifdef X11_DEBUG
     X11_XSynchronize(data->display, True);
 #endif