1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
/* 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/. */
use cstr::cstr;
use nserror::{nsresult, NS_ERROR_SERVICE_NOT_AVAILABLE, NS_OK};
use std::cell::UnsafeCell;
use xpcom::{interfaces::nsIThreadManager, xpcom, xpcom_method};
type IsDoneClosure = dyn FnMut() -> bool + 'static;
#[derive(xpcom)]
#[xpimplements(nsINestedEventLoopCondition)]
#[refcnt = "atomic"]
struct InitEventLoopCondition {
closure: UnsafeCell<Box<IsDoneClosure>>,
}
impl EventLoopCondition {
xpcom_method!(is_done => IsDone() -> bool);
fn is_done(&self) -> Result<bool, nsresult> {
unsafe { Ok((&mut *self.closure.get())()) }
}
}
/// Spin the event loop on the current thread until `pred` returns true.
///
/// # 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<P>(pred: P) -> Result<(), nsresult>
where
P: FnMut() -> bool + 'static,
{
let closure = Box::new(pred) as Box<IsDoneClosure>;
let cond = EventLoopCondition::allocate(InitEventLoopCondition {
closure: UnsafeCell::new(closure),
});
let thread_manager =
xpcom::get_service::<nsIThreadManager>(cstr!("@mozilla.org/thread-manager;1"))
.ok_or(NS_ERROR_SERVICE_NOT_AVAILABLE)?;
thread_manager.SpinEventLoopUntil(cond.coerce()).to_result()
}
|