diff --git a/src/video/android/SDL_androidevents.c b/src/video/android/SDL_androidevents.c index 4bdd717e12..605591b990 100644 --- a/src/video/android/SDL_androidevents.c +++ b/src/video/android/SDL_androidevents.c @@ -178,6 +178,26 @@ static void Android_OnDestroy(void) static void Android_HandleLifecycleEvent(SDL_AndroidLifecycleEvent event) { + // Make sure we're holding the joystick lock before dispatching life cycle events + // This prevents deadlocks of this form: + // 1. SDL locks the event watch list to dispatch the lifecycle event + // 2. a joystick sensor event arrives, taking the joystick lock + // 3. the lifecycle event dispatch calls into the application event + // watcher, which closes joysticks, trying to take the joystick lock + // 4. SDL delivers the sensor event, trying lock the event watch list + // 5. BOOM, deadlock! + // + // There are a few solutions to this, most of which involve unlocking the + // event watch list while delivering events or unlocking the joystick lock + // while delivering joystick events, both of which reduce performance and + // are extremely risky, so we'll do this, which is the least risky option. + // + // If you end up needing to wait for another thread that handles joystick + // events in your life cycle handling, then you can simply unlock joysticks, + // wait for that thread to complete, and then re-lock joysticks. + + SDL_LockJoysticks(); + switch (event) { case SDL_ANDROID_LIFECYCLE_WAKE: // Nothing to do, just return @@ -197,6 +217,8 @@ static void Android_HandleLifecycleEvent(SDL_AndroidLifecycleEvent event) default: break; } + + SDL_UnlockJoysticks(); } static Sint64 GetLifecycleEventTimeout(bool paused, Sint64 timeoutNS)