From 992e4c59bdf03dbec8b1689332ee697623cf6ad2 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
---
src/events/SDL_events.c | 32 ++++++----------------------
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, 9 insertions(+), 47 deletions(-)
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 802c794e916bc..c04d52b28204d 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -1102,16 +1102,11 @@ static void SDL_SendWakeupEvent(void)
return;
}
- 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
+ SDL_Window *wakeup_window = (SDL_Window *)SDL_SetAtomicPointer(&_this->wakeup_window, NULL);
+ if (wakeup_window) {
+ _this->SendWakeupEvent(_this, wakeup_window);
}
- SDL_UnlockMutex(_this->wakeup_lock);
#endif
}
@@ -1549,18 +1544,7 @@ static int SDL_WaitEventTimeout_Device(SDL_VideoDevice *_this, SDL_Window *wakeu
*/
SDL_PumpEventsInternal(true);
- SDL_LockMutex(_this->wakeup_lock);
- {
- status = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
- // 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_EVENT_FIRST, SDL_EVENT_LAST);
if (status < 0) {
// Got an error: return
break;
@@ -1573,8 +1557,6 @@ static int SDL_WaitEventTimeout_Device(SDL_VideoDevice *_this, SDL_Window *wakeu
if (timeoutNS > 0) {
Sint64 elapsed = SDL_GetTicksNS() - start;
if (elapsed >= timeoutNS) {
- // Set wakeup_window to NULL without holding the lock.
- _this->wakeup_window = NULL;
return 0;
}
loop_timeoutNS = (timeoutNS - elapsed);
@@ -1587,9 +1569,9 @@ static int SDL_WaitEventTimeout_Device(SDL_VideoDevice *_this, SDL_Window *wakeu
loop_timeoutNS = poll_intervalNS;
}
}
+ SDL_SetAtomicPointer(&_this->wakeup_window, wakeup_window);
status = _this->WaitEventTimeout(_this, loop_timeoutNS);
- // Set wakeup_window to NULL without holding the lock.
- _this->wakeup_window = NULL;
+ SDL_SetAtomicPointer(&_this->wakeup_window, NULL);
if (status == 0 && poll_intervalNS != SDL_MAX_SINT64 && loop_timeoutNS == poll_intervalNS) {
// 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 f04c91436ac85..4f50e2a680a0d 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -405,8 +405,7 @@ struct SDL_VideoDevice
bool checked_texture_framebuffer;
bool is_dummy;
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_Rect desktop_bounds;
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 294fad5d10e51..069fec4df4ae2 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -4393,9 +4393,7 @@ void SDL_DestroyWindow(SDL_Window *window)
_this->current_glwin = NULL;
}
- if (_this->wakeup_window == window) {
- _this->wakeup_window = NULL;
- }
+ SDL_CompareAndSwapAtomicPointer(&_this->wakeup_window, window, NULL);
// Now invalidate magic
SDL_SetObjectValid(window, SDL_OBJECT_TYPE_WINDOW, false);
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index 81baf7825f43a..aed193df446e1 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -49,9 +49,6 @@ @implementation SDL_CocoaVideoData
static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
{
@autoreleasepool {
- if (device->wakeup_lock) {
- SDL_DestroyMutex(device->wakeup_lock);
- }
CFBridgingRelease(device->internal);
SDL_free(device);
}
@@ -81,7 +78,6 @@ static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
return NULL;
}
device->internal = (SDL_VideoData *)CFBridgingRetain(data);
- device->wakeup_lock = SDL_CreateMutex();
device->system_theme = Cocoa_GetSystemTheme();
// Set the function pointers
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 1713d493ab380..8514a903d6c71 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -447,9 +447,6 @@ static void Wayland_DeleteDevice(SDL_VideoDevice *device)
WAYLAND_wl_display_disconnect(data->display);
SDL_ClearProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER);
}
- if (device->wakeup_lock) {
- SDL_DestroyMutex(device->wakeup_lock);
- }
SDL_free(data);
SDL_free(device);
SDL_WAYLAND_UnloadSymbols();
@@ -576,7 +573,6 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
}
device->internal = 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 25dea1f3b56fd..0c73febd0de13 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -124,9 +124,6 @@ static void WIN_DeleteDevice(SDL_VideoDevice *device)
SDL_UnloadObject(data->dxgiDLL);
}
#endif
- if (device->wakeup_lock) {
- SDL_DestroyMutex(device->wakeup_lock);
- }
SDL_free(device->internal->rawinput);
SDL_free(device->internal);
SDL_free(device);
@@ -152,7 +149,6 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
return NULL;
}
device->internal = data;
- device->wakeup_lock = SDL_CreateMutex();
device->system_theme = WIN_GetSystemTheme();
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index b5dab99b5fbbc..c3adc9413ae83 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -65,9 +65,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->internal);
SDL_free(device);
@@ -148,8 +145,6 @@ static SDL_VideoDevice *X11_CreateDevice(void)
return NULL;
}
- device->wakeup_lock = SDL_CreateMutex();
-
#ifdef X11_DEBUG
X11_XSynchronize(data->display, True);
#endif