Skip to content

Commit 4b4ea7f

Browse files
ribjb55
authored andcommitted
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 c1e9da0 commit 4b4ea7f

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
@@ -327,6 +327,15 @@ static void android_app_set_window(struct android_app* android_app,
327327
ANativeWindow* window) {
328328
LOGV("android_app_set_window called");
329329
pthread_mutex_lock(&android_app->mutex);
330+
331+
// NB: we have to consider that the native thread could have already
332+
// (gracefully) exit (setting android_app->destroyed) and so we need
333+
// to be careful to avoid a deadlock waiting for a thread that's
334+
// already exit.
335+
if (android_app->destroyed) {
336+
pthread_mutex_unlock(&android_app->mutex);
337+
return;
338+
}
330339
if (android_app->pendingWindow != NULL) {
331340
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
332341
}
@@ -343,9 +352,16 @@ static void android_app_set_window(struct android_app* android_app,
343352
static void android_app_set_activity_state(struct android_app* android_app,
344353
int8_t cmd) {
345354
pthread_mutex_lock(&android_app->mutex);
346-
android_app_write_cmd(android_app, cmd);
347-
while (android_app->activityState != cmd) {
348-
pthread_cond_wait(&android_app->cond, &android_app->mutex);
355+
356+
// NB: we have to consider that the native thread could have already
357+
// (gracefully) exit (setting android_app->destroyed) and so we need
358+
// to be careful to avoid a deadlock waiting for a thread that's
359+
// already exit.
360+
if (!android_app->destroyed) {
361+
android_app_write_cmd(android_app, cmd);
362+
while (android_app->activityState != cmd) {
363+
pthread_cond_wait(&android_app->cond, &android_app->mutex);
364+
}
349365
}
350366
pthread_mutex_unlock(&android_app->mutex);
351367
}
@@ -354,6 +370,14 @@ static void android_app_free(struct android_app* android_app) {
354370
int input_buf_idx = 0;
355371

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

404428
struct android_app* android_app = ToApp(activity);
405429
pthread_mutex_lock(&android_app->mutex);
430+
431+
// NB: we have to consider that the native thread could have already
432+
// (gracefully) exit (setting android_app->destroyed) and so we need
433+
// to be careful to avoid a deadlock waiting for a thread that's
434+
// already exit.
435+
if (android_app->destroyed) {
436+
pthread_mutex_unlock(&android_app->mutex);
437+
return;
438+
}
439+
406440
android_app->stateSaved = 0;
407441
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
408442
while (!android_app->stateSaved) {
@@ -508,6 +542,15 @@ static bool onTouchEvent(GameActivity* activity,
508542
struct android_app* android_app = ToApp(activity);
509543
pthread_mutex_lock(&android_app->mutex);
510544

545+
// NB: we have to consider that the native thread could have already
546+
// (gracefully) exit (setting android_app->destroyed) and so we need
547+
// to be careful to avoid a deadlock waiting for a thread that's
548+
// already exit.
549+
if (android_app->destroyed) {
550+
pthread_mutex_unlock(&android_app->mutex);
551+
return false;
552+
}
553+
511554
if (android_app->motionEventFilter != NULL &&
512555
!android_app->motionEventFilter(event)) {
513556
pthread_mutex_unlock(&android_app->mutex);
@@ -586,6 +629,15 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
586629
struct android_app* android_app = ToApp(activity);
587630
pthread_mutex_lock(&android_app->mutex);
588631

632+
// NB: we have to consider that the native thread could have already
633+
// (gracefully) exit (setting android_app->destroyed) and so we need
634+
// to be careful to avoid a deadlock waiting for a thread that's
635+
// already exit.
636+
if (android_app->destroyed) {
637+
pthread_mutex_unlock(&android_app->mutex);
638+
return false;
639+
}
640+
589641
if (android_app->keyEventFilter != NULL &&
590642
!android_app->keyEventFilter(event)) {
591643
pthread_mutex_unlock(&android_app->mutex);
@@ -626,8 +678,9 @@ static void onTextInputEvent(GameActivity* activity,
626678
const GameTextInputState* state) {
627679
struct android_app* android_app = ToApp(activity);
628680
pthread_mutex_lock(&android_app->mutex);
629-
630-
android_app->textInputState = 1;
681+
if (!android_app->destroyed) {
682+
android_app->textInputState = 1;
683+
}
631684
pthread_mutex_unlock(&android_app->mutex);
632685
}
633686

0 commit comments

Comments
 (0)