Skip to content

Commit c10a2fb

Browse files
committed
GameActivity PATH: 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 ab2606a commit c10a2fb

File tree

2 files changed

+59
-9
lines changed

2 files changed

+59
-9
lines changed

android-activity/build.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
#![allow(dead_code)]
22

33
fn build_glue_for_game_activity() {
4-
for f in [
5-
"GameActivity.h",
6-
"GameActivity.cpp",
7-
] {
4+
for f in ["GameActivity.h", "GameActivity.cpp"] {
85
println!("cargo:rerun-if-changed=game-activity-csrc/game-activity/{f}");
96
}
107
cc::Build::new()

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
@@ -308,6 +308,15 @@ static void android_app_set_window(struct android_app* android_app,
308308
ANativeWindow* window) {
309309
LOGV("android_app_set_window called");
310310
pthread_mutex_lock(&android_app->mutex);
311+
312+
// NB: we have to consider that the native thread could have already
313+
// (gracefully) exit (setting android_app->destroyed) and so we need
314+
// to be careful to avoid a deadlock waiting for a thread that's
315+
// already exit.
316+
if (android_app->destroyed) {
317+
pthread_mutex_unlock(&android_app->mutex);
318+
return;
319+
}
311320
if (android_app->pendingWindow != NULL) {
312321
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
313322
}
@@ -324,15 +333,30 @@ static void android_app_set_window(struct android_app* android_app,
324333
static void android_app_set_activity_state(struct android_app* android_app,
325334
int8_t cmd) {
326335
pthread_mutex_lock(&android_app->mutex);
327-
android_app_write_cmd(android_app, cmd);
328-
while (android_app->activityState != cmd) {
329-
pthread_cond_wait(&android_app->cond, &android_app->mutex);
336+
337+
// NB: we have to consider that the native thread could have already
338+
// (gracefully) exit (setting android_app->destroyed) and so we need
339+
// to be careful to avoid a deadlock waiting for a thread that's
340+
// already exit.
341+
if (!android_app->destroyed) {
342+
android_app_write_cmd(android_app, cmd);
343+
while (android_app->activityState != cmd) {
344+
pthread_cond_wait(&android_app->cond, &android_app->mutex);
345+
}
330346
}
331347
pthread_mutex_unlock(&android_app->mutex);
332348
}
333349

334350
static void android_app_free(struct android_app* android_app) {
335351
pthread_mutex_lock(&android_app->mutex);
352+
353+
// It's possible that onDestroy is called after we have already 'destroyed'
354+
// the app (via `android_app_destroy` due to `android_main` returning.
355+
//
356+
// In this case `->destroyed` will already be set (so we won't deadlock in
357+
// the loop below) but we still need to close the messaging fds and finish
358+
// freeing the android_app
359+
336360
android_app_write_cmd(android_app, APP_CMD_DESTROY);
337361
while (!android_app->destroyed) {
338362
pthread_cond_wait(&android_app->cond, &android_app->mutex);
@@ -372,6 +396,16 @@ static void onSaveInstanceState(GameActivity* activity,
372396

373397
struct android_app* android_app = ToApp(activity);
374398
pthread_mutex_lock(&android_app->mutex);
399+
400+
// NB: we have to consider that the native thread could have already
401+
// (gracefully) exit (setting android_app->destroyed) and so we need
402+
// to be careful to avoid a deadlock waiting for a thread that's
403+
// already exit.
404+
if (android_app->destroyed) {
405+
pthread_mutex_unlock(&android_app->mutex);
406+
return;
407+
}
408+
375409
android_app->stateSaved = 0;
376410
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
377411
while (!android_app->stateSaved) {
@@ -481,6 +515,15 @@ static bool onTouchEvent(GameActivity* activity,
481515
struct android_app* android_app = ToApp(activity);
482516
pthread_mutex_lock(&android_app->mutex);
483517

518+
// NB: we have to consider that the native thread could have already
519+
// (gracefully) exit (setting android_app->destroyed) and so we need
520+
// to be careful to avoid a deadlock waiting for a thread that's
521+
// already exit.
522+
if (android_app->destroyed) {
523+
pthread_mutex_unlock(&android_app->mutex);
524+
return false;
525+
}
526+
484527
if (android_app->motionEventFilter != NULL &&
485528
!android_app->motionEventFilter(event)) {
486529
pthread_mutex_unlock(&android_app->mutex);
@@ -563,6 +606,15 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
563606
struct android_app* android_app = ToApp(activity);
564607
pthread_mutex_lock(&android_app->mutex);
565608

609+
// NB: we have to consider that the native thread could have already
610+
// (gracefully) exit (setting android_app->destroyed) and so we need
611+
// to be careful to avoid a deadlock waiting for a thread that's
612+
// already exit.
613+
if (android_app->destroyed) {
614+
pthread_mutex_unlock(&android_app->mutex);
615+
return false;
616+
}
617+
566618
if (android_app->keyEventFilter != NULL &&
567619
!android_app->keyEventFilter(event)) {
568620
pthread_mutex_unlock(&android_app->mutex);
@@ -599,8 +651,9 @@ static void onTextInputEvent(GameActivity* activity,
599651
const GameTextInputState* state) {
600652
struct android_app* android_app = ToApp(activity);
601653
pthread_mutex_lock(&android_app->mutex);
602-
603-
android_app->textInputState = 1;
654+
if (!android_app->destroyed) {
655+
android_app->textInputState = 1;
656+
}
604657
pthread_mutex_unlock(&android_app->mutex);
605658
}
606659

0 commit comments

Comments
 (0)