summaryrefslogtreecommitdiffstats
path: root/xpcom/rust/moz_task/src/event_loop.rs
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/rust/moz_task/src/event_loop.rs')
-rw-r--r--xpcom/rust/moz_task/src/event_loop.rs66
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)
+}