Reduce the chance of destroying the joystick mutex while it's in use

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

(cherry picked from commit 6390165fd4c193631d6780758a4aeec0d02b90eb)
This commit is contained in:
Sam Lantinga 2023-06-12 15:26:34 -07:00
parent 2afb49ba9a
commit e4e0a12901

View file

@ -114,6 +114,7 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
static static
#endif #endif
SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */ SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */
static SDL_atomic_t SDL_joystick_lock_pending;
static int SDL_joysticks_locked; static int SDL_joysticks_locked;
static SDL_bool SDL_joysticks_initialized; static SDL_bool SDL_joysticks_initialized;
static SDL_bool SDL_joysticks_quitting = SDL_FALSE; static SDL_bool SDL_joysticks_quitting = SDL_FALSE;
@ -143,23 +144,35 @@ SDL_bool SDL_JoysticksQuitting(void)
void SDL_LockJoysticks(void) void SDL_LockJoysticks(void)
{ {
SDL_AtomicIncRef(&SDL_joystick_lock_pending);
SDL_LockMutex(SDL_joystick_lock); SDL_LockMutex(SDL_joystick_lock);
SDL_AtomicDecRef(&SDL_joystick_lock_pending);
++SDL_joysticks_locked; ++SDL_joysticks_locked;
} }
void SDL_UnlockJoysticks(void) void SDL_UnlockJoysticks(void)
{ {
SDL_mutex *joystick_lock = SDL_joystick_lock;
SDL_bool last_unlock = SDL_FALSE;
--SDL_joysticks_locked; --SDL_joysticks_locked;
SDL_UnlockMutex(SDL_joystick_lock); if (!SDL_joysticks_initialized) {
if (!SDL_joysticks_locked && SDL_AtomicGet(&SDL_joystick_lock_pending) == 0) {
/* NOTE: There's a small window here where another thread could lock the mutex */
SDL_joystick_lock = NULL;
last_unlock = SDL_TRUE;
}
}
SDL_UnlockMutex(joystick_lock);
/* The last unlock after joysticks are uninitialized will cleanup the mutex, /* The last unlock after joysticks are uninitialized will cleanup the mutex,
* allowing applications to lock joysticks while reinitializing the system. * allowing applications to lock joysticks while reinitializing the system.
*/ */
if (SDL_joystick_lock && !SDL_joysticks_locked && !SDL_joysticks_initialized) { if (last_unlock) {
SDL_DestroyMutex(SDL_joystick_lock); SDL_DestroyMutex(joystick_lock);
SDL_joystick_lock = NULL;
} }
} }