Skip to content

Commit 2a2f276

Browse files
committed
GameActivity PATCH: fix deadlocks in java callbacks after app destroyed
This ensures that any java Activity callbacks take into account the possibility that the `android_app` may have already been marked destroyed if `android_main` has returned - and so they mustn't block and wait for a thread that is no longer running.
1 parent d2d1815 commit 2a2f276

File tree

1 file changed

+58
-5
lines changed

1 file changed

+58
-5
lines changed

android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.c

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,15 @@ static void android_app_set_window(struct android_app* android_app,
326326
ANativeWindow* window) {
327327
LOGV("android_app_set_window called");
328328
pthread_mutex_lock(&android_app->mutex);
329+
330+
// NB: we have to consider that the native thread could have already
331+
// (gracefully) exit (setting android_app->destroyed) and so we need
332+
// to be careful to avoid a deadlock waiting for a thread that's
333+
// already exit.
334+
if (android_app->destroyed) {
335+
pthread_mutex_unlock(&android_app->mutex);
336+
return;
337+
}
329338
if (android_app->pendingWindow != NULL) {
330339
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
331340
}
@@ -342,9 +351,16 @@ static void android_app_set_window(struct android_app* android_app,
342351
static void android_app_set_activity_state(struct android_app* android_app,
343352
int8_t cmd) {
344353
pthread_mutex_lock(&android_app->mutex);
345-
android_app_write_cmd(android_app, cmd);
346-
while (android_app->activityState != cmd) {
347-
pthread_cond_wait(&android_app->cond, &android_app->mutex);
354+
355+
// NB: we have to consider that the native thread could have already
356+
// (gracefully) exit (setting android_app->destroyed) and so we need
357+
// to be careful to avoid a deadlock waiting for a thread that's
358+
// already exit.
359+
if (!android_app->destroyed) {
360+
android_app_write_cmd(android_app, cmd);
361+
while (android_app->activityState != cmd) {
362+
pthread_cond_wait(&android_app->cond, &android_app->mutex);
363+
}
348364
}
349365
pthread_mutex_unlock(&android_app->mutex);
350366
}
@@ -353,6 +369,14 @@ static void android_app_free(struct android_app* android_app) {
353369
int input_buf_idx = 0;
354370

355371
pthread_mutex_lock(&android_app->mutex);
372+
373+
// It's possible that onDestroy is called after we have already 'destroyed'
374+
// the app (via `android_app_destroy` due to `android_main` returning.
375+
//
376+
// In this case `->destroyed` will already be set (so we won't deadlock in
377+
// the loop below) but we still need to close the messaging fds and finish
378+
// freeing the android_app
379+
356380
android_app_write_cmd(android_app, APP_CMD_DESTROY);
357381
while (!android_app->destroyed) {
358382
pthread_cond_wait(&android_app->cond, &android_app->mutex);
@@ -400,6 +424,16 @@ static void onSaveInstanceState(GameActivity* activity,
400424

401425
struct android_app* android_app = ToApp(activity);
402426
pthread_mutex_lock(&android_app->mutex);
427+
428+
// NB: we have to consider that the native thread could have already
429+
// (gracefully) exit (setting android_app->destroyed) and so we need
430+
// to be careful to avoid a deadlock waiting for a thread that's
431+
// already exit.
432+
if (android_app->destroyed) {
433+
pthread_mutex_unlock(&android_app->mutex);
434+
return;
435+
}
436+
403437
android_app->stateSaved = 0;
404438
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
405439
while (!android_app->stateSaved) {
@@ -505,6 +539,15 @@ static bool onTouchEvent(GameActivity* activity,
505539
struct android_app* android_app = ToApp(activity);
506540
pthread_mutex_lock(&android_app->mutex);
507541

542+
// NB: we have to consider that the native thread could have already
543+
// (gracefully) exit (setting android_app->destroyed) and so we need
544+
// to be careful to avoid a deadlock waiting for a thread that's
545+
// already exit.
546+
if (android_app->destroyed) {
547+
pthread_mutex_unlock(&android_app->mutex);
548+
return false;
549+
}
550+
508551
if (android_app->motionEventFilter != NULL &&
509552
!android_app->motionEventFilter(event)) {
510553
pthread_mutex_unlock(&android_app->mutex);
@@ -582,6 +625,15 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
582625
struct android_app* android_app = ToApp(activity);
583626
pthread_mutex_lock(&android_app->mutex);
584627

628+
// NB: we have to consider that the native thread could have already
629+
// (gracefully) exit (setting android_app->destroyed) and so we need
630+
// to be careful to avoid a deadlock waiting for a thread that's
631+
// already exit.
632+
if (android_app->destroyed) {
633+
pthread_mutex_unlock(&android_app->mutex);
634+
return false;
635+
}
636+
585637
if (android_app->keyEventFilter != NULL &&
586638
!android_app->keyEventFilter(event)) {
587639
pthread_mutex_unlock(&android_app->mutex);
@@ -620,8 +672,9 @@ static void onTextInputEvent(GameActivity* activity,
620672
const GameTextInputState* state) {
621673
struct android_app* android_app = ToApp(activity);
622674
pthread_mutex_lock(&android_app->mutex);
623-
624-
android_app->textInputState = 1;
675+
if (!android_app->destroyed) {
676+
android_app->textInputState = 1;
677+
}
625678
pthread_mutex_unlock(&android_app->mutex);
626679
}
627680

0 commit comments

Comments
 (0)