Skip to content

Commit c841f66

Browse files
committed
native_activity: Only wait for state to update while main thread is running
We see that some Android callbacks like `onStart()` deadlock, specifically when returning out of the main thread before running any event loop (but likely also whenver terminating the event loop), because they don't check if the thread is still even running and are otherwise guaranteed receive an `activity_state` update or other state change to unblock themselves. This is a followup to [#94] which only concerned itself with a deadlock caused by a destructor not running because that very object was kept alive to poll on the `destroyed` field that destructor was supposed to set, but its new `thread_state` can be reused to disable these condvar waits when the "sending" thread has disappeard. Separately, that PR mentions `Activity` recreates because of configuration changes which isn't supported anyway because `Activity` is still wrongly assumed to be a global singleton. [#94]: https://github.com/rust-mobile/android-activity/pull/94
1 parent 51d05d4 commit c841f66

File tree

1 file changed

+11
-5
lines changed
  • android-activity/src/native_activity

1 file changed

+11
-5
lines changed

android-activity/src/native_activity/glue.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ impl WaitableNativeActivityState {
447447

448448
guard.pending_input_queue = input_queue;
449449
guard.write_cmd(AppCmd::InputQueueChanged);
450-
while guard.input_queue != guard.pending_input_queue {
450+
while guard.thread_state == NativeThreadState::Running
451+
&& guard.input_queue != guard.pending_input_queue
452+
{
451453
guard = self.cond.wait(guard).unwrap();
452454
}
453455
guard.pending_input_queue = ptr::null_mut();
@@ -468,7 +470,9 @@ impl WaitableNativeActivityState {
468470
if guard.pending_window.is_some() {
469471
guard.write_cmd(AppCmd::InitWindow);
470472
}
471-
while guard.window != guard.pending_window {
473+
while guard.thread_state == NativeThreadState::Running
474+
&& guard.window != guard.pending_window
475+
{
472476
guard = self.cond.wait(guard).unwrap();
473477
}
474478
guard.pending_window = None;
@@ -492,7 +496,7 @@ impl WaitableNativeActivityState {
492496
};
493497
guard.write_cmd(cmd);
494498

495-
while guard.activity_state != state {
499+
while guard.thread_state == NativeThreadState::Running && guard.activity_state != state {
496500
guard = self.cond.wait(guard).unwrap();
497501
}
498502
}
@@ -505,7 +509,7 @@ impl WaitableNativeActivityState {
505509
// this to be None
506510
debug_assert!(!guard.app_has_saved_state, "SaveState request clash");
507511
guard.write_cmd(AppCmd::SaveState);
508-
while !guard.app_has_saved_state {
512+
while guard.thread_state == NativeThreadState::Running && !guard.app_has_saved_state {
509513
guard = self.cond.wait(guard).unwrap();
510514
}
511515
guard.app_has_saved_state = false;
@@ -560,7 +564,9 @@ impl WaitableNativeActivityState {
560564
pub fn notify_main_thread_stopped_running(&self) {
561565
let mut guard = self.mutex.lock().unwrap();
562566
guard.thread_state = NativeThreadState::Stopped;
563-
self.cond.notify_one();
567+
// Notify all waiters to unblock any Android callbacks that would otherwise be waiting
568+
// indefinitely for the now-stopped (!) main thread.
569+
self.cond.notify_all();
564570
}
565571

566572
pub unsafe fn pre_exec_cmd(

0 commit comments

Comments
 (0)