diff options
Diffstat (limited to 'xpcom/rust/moz_task/src/event_loop.rs')
-rw-r--r-- | xpcom/rust/moz_task/src/event_loop.rs | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/xpcom/rust/moz_task/src/event_loop.rs b/xpcom/rust/moz_task/src/event_loop.rs new file mode 100644 index 0000000000..f8d113ed57 --- /dev/null +++ b/xpcom/rust/moz_task/src/event_loop.rs @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate nsstring; + +use cstr::cstr; +use nserror::{nsresult, NS_ERROR_SERVICE_NOT_AVAILABLE, NS_ERROR_UNEXPECTED, NS_OK}; +use nsstring::*; +use std::cell::RefCell; +use std::future::Future; +use xpcom::{interfaces::nsIThreadManager, xpcom, xpcom_method}; + +#[xpcom(implement(nsINestedEventLoopCondition), nonatomic)] +struct FutureCompleteCondition<T: 'static> { + value: RefCell<Option<T>>, +} + +impl<T: 'static> FutureCompleteCondition<T> { + xpcom_method!(is_done => IsDone() -> bool); + fn is_done(&self) -> Result<bool, nsresult> { + Ok(self.value.borrow().is_some()) + } +} + +/// Spin the event loop on the current thread until `future` is resolved. +/// +/// # Safety +/// +/// Spinning a nested event loop should always be avoided when possible, as it +/// can cause hangs, break JS run-to-completion guarantees, and break other C++ +/// code currently on the stack relying on heap invariants. While in a pure-rust +/// codebase this method would only be ill-advised and not technically "unsafe", +/// it is marked as unsafe due to the potential for triggering unsafety in +/// unrelated C++ code. +pub unsafe fn spin_event_loop_until<F>( + reason: &'static str, + future: F, +) -> Result<F::Output, nsresult> +where + F: Future + 'static, + F::Output: 'static, +{ + let thread_manager = + xpcom::get_service::<nsIThreadManager>(cstr!("@mozilla.org/thread-manager;1")) + .ok_or(NS_ERROR_SERVICE_NOT_AVAILABLE)?; + + let cond = FutureCompleteCondition::<F::Output>::allocate(InitFutureCompleteCondition { + value: RefCell::new(None), + }); + + // Spawn our future onto the current thread event loop, and record the + // completed value as it completes. + let cond2 = cond.clone(); + crate::spawn_local(reason, async move { + let rv = future.await; + *cond2.value.borrow_mut() = Some(rv); + }) + .detach(); + + thread_manager + .SpinEventLoopUntil(&*nsCStr::from(reason), cond.coerce()) + .to_result()?; + let rv = cond.value.borrow_mut().take(); + rv.ok_or(NS_ERROR_UNEXPECTED) +} |