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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::future::Future;
use std::ptr;
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
use crate::{check_status, sys, JsDeferred, JsUnknown, NapiValue, Result};
pub(crate) static mut RT: Lazy<Option<Runtime>> = Lazy::new(|| {
let runtime = tokio::runtime::Runtime::new().expect("Create tokio runtime failed");
Some(runtime)
});
#[cfg(windows)]
pub(crate) static RT_REFERENCE_COUNT: std::sync::atomic::AtomicUsize =
std::sync::atomic::AtomicUsize::new(0);
#[cfg(windows)]
pub(crate) unsafe extern "C" fn drop_runtime(arg: *mut std::ffi::c_void) {
use std::sync::atomic::Ordering;
if RT_REFERENCE_COUNT.fetch_sub(1, Ordering::SeqCst) == 1 {
if let Some(rt) = Lazy::get_mut(unsafe { &mut RT }) {
rt.take();
}
}
unsafe {
let env: sys::napi_env = arg as *mut sys::napi_env__;
sys::napi_remove_env_cleanup_hook(env, Some(drop_runtime), arg);
}
}
pub fn spawn<F>(fut: F) -> tokio::task::JoinHandle<F::Output>
where
F: 'static + Send + Future<Output = ()>,
{
unsafe { RT.as_ref() }.unwrap().spawn(fut)
}
pub fn block_on<F>(fut: F) -> F::Output
where
F: 'static + Send + Future<Output = ()>,
{
unsafe { RT.as_ref() }.unwrap().block_on(fut)
}
#[inline]
pub fn within_runtime_if_available<F: FnOnce() -> T, T>(f: F) -> T {
let _rt_guard = unsafe { RT.as_ref() }.unwrap().enter();
f()
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn execute_tokio_future<
Data: 'static + Send,
Fut: 'static + Send + Future<Output = Result<Data>>,
Resolver: 'static + Send + Sync + FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>,
>(
env: sys::napi_env,
fut: Fut,
resolver: Resolver,
) -> Result<sys::napi_value> {
let mut promise = ptr::null_mut();
let mut deferred = ptr::null_mut();
check_status!(unsafe { sys::napi_create_promise(env, &mut deferred, &mut promise) })?;
let (deferred, promise) = JsDeferred::new(env)?;
spawn(async move {
match fut.await {
Ok(v) => deferred.resolve(|env| {
resolver(env.raw(), v).map(|v| unsafe { JsUnknown::from_raw_unchecked(env.raw(), v) })
}),
Err(e) => deferred.reject(e),
}
});
Ok(promise.0.value)
}