From 4177e06c099925ffb567d5adab6c415df67c6d88 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 19 May 2025 13:53:33 -0700
Subject: [PATCH] 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)
---
src/events/SDL_events.c | 33 +++++++---------------------
src/video/SDL_sysvideo.h | 3 +--
src/video/SDL_video.c | 4 +---
src/video/cocoa/SDL_cocoavideo.m | 4 ----
src/video/wayland/SDL_waylandvideo.c | 4 ----
src/video/windows/SDL_windowsvideo.c | 4 ----
src/video/x11/SDL_x11video.c | 5 -----
7 files changed, 10 insertions(+), 47 deletions(-)
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 3dcd9a8f66f57..271e2504b9491 100644
--- a/src/events/SDL_events.c
+++ b/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;
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 22bca8a222c92..faf598be388b8 100644
--- a/src/video/SDL_sysvideo.h
+++ b/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;
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 01023753c6e49..2f6c631d12f40 100644
--- a/src/video/SDL_video.c
+++ b/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;
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index 811e85dbb9703..4ca68ba836fa6 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -48,9 +48,6 @@ @implementation SDL_VideoData
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 void Cocoa_DeleteDevice(SDL_VideoDevice * device)
return NULL;
}
device->driverdata = (void *)CFBridgingRetain(data);
- device->wakeup_lock = SDL_CreateMutex();
/* Set the function pointers */
device->VideoInit = Cocoa_VideoInit;
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 1d525be496542..0549520d61acd 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/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;
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index 7c0265805485b..6c71bdcb60b40 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/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");
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 9023add317d7c..b43da511681dd 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/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