-
Notifications
You must be signed in to change notification settings - Fork 65
native_activity: Only wait for state to update while main thread is running #187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
c841f66 to
5a82603
Compare
| guard.write_cmd(AppCmd::InitWindow); | ||
| } | ||
| while guard.window != guard.pending_window { | ||
| while guard.thread_state == NativeThreadState::Running |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess all of these (maybe with an exception for input events) should probably be checking for != NativeThreadState::Stopped so that callbacks effectively buffer during initialization and allow the app to start handling events or else exit and have this transition to Stopped.
The protocol for notifying the app about a window is supposed to be synchronous and it would be a significant change to have these start being async until the thread is initialized and 'running'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsure about that. Android so far seems to service all callbacks into Activity from the same "thread", i.e. the one that also calls onCreate. After all, before onCreate returns we haven't assigned any callbacks yet to ANativeActivity::callbacks, so it "must" wait.
That function only returns when the state moved on from Init, so I don't think any other function should be waiting for this:
android-activity/android-activity/src/native_activity/glue.rs
Lines 914 to 918 in 1652ebb
| // Don't specifically wait for `Running` just in case `android_main` returns | |
| // immediately and the state is set to `Stopped` | |
| while guard.thread_state == NativeThreadState::Init { | |
| guard = jvm_glue.cond.wait(guard).unwrap(); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aah, yep that makes sense.
Btw, it's explicitly documented that polling is only allowed on the Ref:
|
|
It looks like I put a warning that
|
…unning 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 whenever 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 disappeared. 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://togithub.com/rust-mobile/android-activity/pull/94
5a82603 to
9afaca8
Compare
|
Right @rib, I may have written that threading possibility out in my PR/commit description from the perspective of what is technically possible on Android, not what In that sense it's funny that this API portion of Anyway, it is very easy to make that panicking threat real: the check for |
rib
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks good to me!
|
Awesome! I'll merge and backport this when back at my own computer :) |
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 whenever terminating the event loop), because they don't check if the thread is still even running and are otherwise guaranteed receive anactivity_stateupdate 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
destroyedfield that destructor was supposed to set, but its newthread_statecan be reused to disable these condvar waits when the "sending" thread has disappeared.Separately, that PR mentions
Activityrecreates because of configuration changes which isn't supported anyway becauseActivityis still wrongly assumed to be a global singleton (I am still working on fixing all this 🤞!).Note that I'm not at all going to claim this to be really fixing anything, it's more of a bandaid on top of another bandaid with doubts if it'll stick.
#94 seems to have only considered thread state, which makes sense given that
finish()is also unconditionally called when the "main" thread created byandroid-activityreturns, but the mentioned missingdrop()handling ofWaitableNativeActivityStatealso seems to very specifically exist becauseAndroidAppis explicitly and intentionally cheaplyCloneable andSend/Sync. If anything the user could move things to a different thread to handle their looping behaviour there. Liveness of these objects is an indicator that the user may/will still poll elsewhere, or at least has the ability to. That should likely have been the predicate to exit out of variouswhileloops rather than the thread state, but here we are.Add to that the request to not start a thread for the user in the first place (requires a redesign to signify that users must timely return from their not-really-
main()-anymore), so that they can load things fromJNIEnvwhich uses prior Java stackframes to resolve classes that cannot otherwise be found without poking atContext.getClassLoader()...