Skip to content

Commit eb97a1f

Browse files
committed
ndk-glue: Don't return from onCreate until LOOPER is created
Android only performs additional initialization like providing an input queue through `onInputQueueCreated` after the main initialization function (`ANativeActivity_onCreate`) returns. In some cases it is possible that the main thread spawned here hasn't attached a looper to its thread and assigned it to the accompanying `LOOPER` static variable yet, resulting in a `None` unwrap when Android provides us with an input queue. This race condition is simply solved by not returning from `fn init()` (`ANativeActivity_onCreate`) until the thread has finished its setup, through wrapping `LOOPER` in a `Mutex` and using a condition variable to signal when it is ready. This condition is intentionally not exposed to `on_input_queue_created` may we ever have a cleanup procedure that sets `LOOPER` back to `None`; any call to `onInputQueueCreated` in that state would be an error and should not block indefinitely. The `LOOPER` should simply be ready by the time `fn init()` returns. Fixes rust-mobile#116
1 parent e5f0c7e commit eb97a1f

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

ndk-glue/src/lib.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::io::{BufRead, BufReader};
1111
use std::os::raw;
1212
use std::os::unix::prelude::*;
1313
use std::ptr::NonNull;
14-
use std::sync::{RwLock, RwLockReadGuard};
14+
use std::sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard};
1515
use std::thread;
1616

1717
pub use ndk_macro::main;
@@ -45,10 +45,10 @@ lazy_static! {
4545
static ref NATIVE_WINDOW: RwLock<Option<NativeWindow>> = Default::default();
4646
static ref INPUT_QUEUE: RwLock<Option<InputQueue>> = Default::default();
4747
static ref CONTENT_RECT: RwLock<Rect> = Default::default();
48+
static ref LOOPER: Mutex<Option<ForeignLooper>> = Default::default();
4849
}
4950

5051
static mut NATIVE_ACTIVITY: Option<NativeActivity> = None;
51-
static mut LOOPER: Option<ForeignLooper> = None;
5252

5353
pub fn native_activity() -> &'static NativeActivity {
5454
unsafe { NATIVE_ACTIVITY.as_ref().unwrap() }
@@ -172,6 +172,9 @@ pub unsafe fn init(
172172
}
173173
});
174174

175+
let looper_ready = Arc::new(Condvar::new());
176+
let signal_looper_ready = looper_ready.clone();
177+
175178
thread::spawn(move || {
176179
let looper = ThreadLooper::prepare();
177180
let foreign = looper.into_foreign();
@@ -183,9 +186,24 @@ pub unsafe fn init(
183186
std::ptr::null_mut(),
184187
)
185188
.unwrap();
186-
LOOPER = Some(foreign);
189+
190+
{
191+
let mut locked_looper = LOOPER.lock().unwrap();
192+
*locked_looper = Some(foreign);
193+
signal_looper_ready.notify_one();
194+
}
195+
187196
main()
188197
});
198+
199+
// Don't return from this function (`ANativeActivity_onCreate`) until the thread
200+
// has created its `ThreadLooper` and assigned it to the static `LOOPER`
201+
// variable. It will be used from `on_input_queue_created` as soon as this
202+
// function returns.
203+
let locked_looper = LOOPER.lock().unwrap();
204+
let _mutex_guard = looper_ready
205+
.wait_while(locked_looper, |looper| looper.is_none())
206+
.unwrap();
189207
}
190208

191209
unsafe extern "C" fn on_start(activity: *mut ANativeActivity) {
@@ -269,7 +287,8 @@ unsafe extern "C" fn on_input_queue_created(
269287
queue: *mut AInputQueue,
270288
) {
271289
let input_queue = InputQueue::from_ptr(NonNull::new(queue).unwrap());
272-
let looper = LOOPER.as_ref().unwrap();
290+
let locked_looper = LOOPER.lock().unwrap();
291+
let looper = locked_looper.as_ref().expect("Looper does not exist, ");
273292
input_queue.attach_looper(looper, NDK_GLUE_LOOPER_INPUT_QUEUE_IDENT);
274293
*INPUT_QUEUE.write().unwrap() = Some(input_queue);
275294
wake(activity, Event::InputQueueCreated);

ndk/src/looper.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl ThreadLooper {
163163
}
164164
}
165165

166-
/// An `ALooper`, not necessarily allociated with the current thread.
166+
/// An `ALooper`, not necessarily allocated with the current thread.
167167
#[derive(Debug)]
168168
pub struct ForeignLooper {
169169
ptr: NonNull<ffi::ALooper>,

0 commit comments

Comments
 (0)