2023-11-06 11:58:23 +09:00
|
|
|
use std::{future::Future, marker::PhantomData, sync::RwLock};
|
2021-12-22 00:22:23 +09:00
|
|
|
|
2022-06-25 12:19:45 +09:00
|
|
|
use once_cell::sync::Lazy;
|
2022-12-16 15:32:01 +09:00
|
|
|
use tokio::runtime::Runtime;
|
2021-10-20 01:02:47 +09:00
|
|
|
|
2022-12-29 01:15:55 +09:00
|
|
|
use crate::{sys, JsDeferred, JsUnknown, NapiValue, Result};
|
2021-10-20 01:02:47 +09:00
|
|
|
|
2023-03-28 13:03:00 +09:00
|
|
|
fn create_runtime() -> Option<Runtime> {
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(not(target_family = "wasm"))]
|
2023-03-20 19:42:27 +09:00
|
|
|
{
|
|
|
|
let runtime = tokio::runtime::Runtime::new().expect("Create tokio runtime failed");
|
|
|
|
Some(runtime)
|
|
|
|
}
|
|
|
|
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(target_family = "wasm")]
|
2023-03-20 19:42:27 +09:00
|
|
|
{
|
|
|
|
tokio::runtime::Builder::new_current_thread()
|
|
|
|
.enable_all()
|
|
|
|
.build()
|
|
|
|
.ok()
|
|
|
|
}
|
2023-03-28 13:03:00 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) static RT: Lazy<RwLock<Option<Runtime>>> = Lazy::new(|| RwLock::new(create_runtime()));
|
2021-10-20 01:02:47 +09:00
|
|
|
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(not(any(target_os = "macos", target_family = "wasm")))]
|
fix: prevent crashing when napi_register_module_v1 is called twice (#1554)
* fix: prevent crashing when napi_register_module_v1 is called twice
Currently napi-rs addons can lead to the Node.js process aborting with
the following error when initialising the addon on Windows:
```
c:\ws\src\cleanup_queue-inl.h:32: Assertion `(insertion_info.second)
== (true)' failed.
```
This happens because `napi_add_env_cleanup_hook` must not be called
with the same arguments multiple times unless the previously scheduled
cleanup hook with the same arguments was already executed. However,
the cleanup hook added by `napi_register_module_v1` in napi-rs on
Windows was always created with `ptr::null_mut()` as an argument.
One case where this causes a problem is when using the addon from
multiple contexts (e.g. Node.js worker threads) at the same
time. However, Node.js doesn't provide any guarantees that the N-API
addon initialisation code will run only once even per thread and
context. In fact, it's totally valid to run `process.dlopen()`
multiple times from JavaScript land in Node.js, and this will lead to
the initialisation code being run multiple times as different
`exports` objects may need to be populated. This may happen in
numerous cases, e.g.:
- When it's not possible or not desirable to use `require()` and users
must resort to using `process.dlopen()` (one use case is passing
non-default flags to `dlopen(3)`, another is ES modules). Caching
the results of `process.dlopen()` to avoid running it more than once
may not always be possible reliably in all cases (for example,
because of Jest sandbox).
- When the `require` cache is cleared.
- On Windows: `require("./addon.node")` and then
`require(path.toNamespacedPath("./addon.node"))`.
Another issue is fixed inside `napi::tokio_runtime::drop_runtime`:
there's no need to call `napi_remove_env_cleanup_hook` (it's only
useful to cancel the hooks that haven't been executed yet). Null
pointer retrieved from `arg` was being passed as the `env` argument of
that function, so it didn't do anything and just returned
`napi_invalid_arg`.
This patch makes `napi_register_module_v1` use a counter as the
cleanup hook argument, so that the value is always different. An
alternative might have been to use a higher-level abstraction around
`sys::napi_env_cleanup_hook` that would take ownership of a boxed
closure, if there is something like this in the API already. Another
alternative could have been to heap-allocate a value so that we would
have a unique valid memory address.
The patch also contains a minor code cleanup related to
`RT_REFERENCE_COUNT` along the way: the counter is encapsulated inside
its module and `ensure_runtime` takes care of incrementing it, and
less strict memory ordering is now used as there's no need for
`SeqCst` here. If desired, it can be further optimised to
`Ordering::Release` and a separate acquire fence inside the if
statement in `drop_runtime`, as `AcqRel` for every decrement is also a
bit stricter than necessary (although simpler). These changes are not
necessary to fix the issue and can be extracted to a separate patch.
At first it was tempting to use the loaded value of
`RT_REFERENCE_COUNT` as the argument for the cleanup hook but it would
have been wrong: a simple counterexample is the following sequence:
1. init in the first context (queue: 0)
2. init in the second context (queue: 0, 1)
3. destroy the first context (queue: 1)
4. init in the third context (queue: 1, 1)
* test(napi): unload test was excluded unexpected
---------
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-04-09 00:08:48 +09:00
|
|
|
static RT_REFERENCE_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
2022-12-16 21:07:22 +09:00
|
|
|
|
2023-03-28 13:03:00 +09:00
|
|
|
/// Ensure that the Tokio runtime is initialized.
|
|
|
|
/// In windows the Tokio runtime will be dropped when Node env exits.
|
|
|
|
/// But in Electron renderer process, the Node env will exits and recreate when the window reloads.
|
|
|
|
/// So we need to ensure that the Tokio runtime is initialized when the Node env is created.
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(not(any(target_os = "macos", target_family = "wasm")))]
|
2023-03-28 13:03:00 +09:00
|
|
|
pub(crate) fn ensure_runtime() {
|
fix: prevent crashing when napi_register_module_v1 is called twice (#1554)
* fix: prevent crashing when napi_register_module_v1 is called twice
Currently napi-rs addons can lead to the Node.js process aborting with
the following error when initialising the addon on Windows:
```
c:\ws\src\cleanup_queue-inl.h:32: Assertion `(insertion_info.second)
== (true)' failed.
```
This happens because `napi_add_env_cleanup_hook` must not be called
with the same arguments multiple times unless the previously scheduled
cleanup hook with the same arguments was already executed. However,
the cleanup hook added by `napi_register_module_v1` in napi-rs on
Windows was always created with `ptr::null_mut()` as an argument.
One case where this causes a problem is when using the addon from
multiple contexts (e.g. Node.js worker threads) at the same
time. However, Node.js doesn't provide any guarantees that the N-API
addon initialisation code will run only once even per thread and
context. In fact, it's totally valid to run `process.dlopen()`
multiple times from JavaScript land in Node.js, and this will lead to
the initialisation code being run multiple times as different
`exports` objects may need to be populated. This may happen in
numerous cases, e.g.:
- When it's not possible or not desirable to use `require()` and users
must resort to using `process.dlopen()` (one use case is passing
non-default flags to `dlopen(3)`, another is ES modules). Caching
the results of `process.dlopen()` to avoid running it more than once
may not always be possible reliably in all cases (for example,
because of Jest sandbox).
- When the `require` cache is cleared.
- On Windows: `require("./addon.node")` and then
`require(path.toNamespacedPath("./addon.node"))`.
Another issue is fixed inside `napi::tokio_runtime::drop_runtime`:
there's no need to call `napi_remove_env_cleanup_hook` (it's only
useful to cancel the hooks that haven't been executed yet). Null
pointer retrieved from `arg` was being passed as the `env` argument of
that function, so it didn't do anything and just returned
`napi_invalid_arg`.
This patch makes `napi_register_module_v1` use a counter as the
cleanup hook argument, so that the value is always different. An
alternative might have been to use a higher-level abstraction around
`sys::napi_env_cleanup_hook` that would take ownership of a boxed
closure, if there is something like this in the API already. Another
alternative could have been to heap-allocate a value so that we would
have a unique valid memory address.
The patch also contains a minor code cleanup related to
`RT_REFERENCE_COUNT` along the way: the counter is encapsulated inside
its module and `ensure_runtime` takes care of incrementing it, and
less strict memory ordering is now used as there's no need for
`SeqCst` here. If desired, it can be further optimised to
`Ordering::Release` and a separate acquire fence inside the if
statement in `drop_runtime`, as `AcqRel` for every decrement is also a
bit stricter than necessary (although simpler). These changes are not
necessary to fix the issue and can be extracted to a separate patch.
At first it was tempting to use the loaded value of
`RT_REFERENCE_COUNT` as the argument for the cleanup hook but it would
have been wrong: a simple counterexample is the following sequence:
1. init in the first context (queue: 0)
2. init in the second context (queue: 0, 1)
3. destroy the first context (queue: 1)
4. init in the third context (queue: 1, 1)
* test(napi): unload test was excluded unexpected
---------
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-04-09 00:08:48 +09:00
|
|
|
use std::sync::atomic::Ordering;
|
|
|
|
|
2023-03-28 13:03:00 +09:00
|
|
|
let mut rt = RT.write().unwrap();
|
|
|
|
if rt.is_none() {
|
|
|
|
*rt = create_runtime();
|
|
|
|
}
|
fix: prevent crashing when napi_register_module_v1 is called twice (#1554)
* fix: prevent crashing when napi_register_module_v1 is called twice
Currently napi-rs addons can lead to the Node.js process aborting with
the following error when initialising the addon on Windows:
```
c:\ws\src\cleanup_queue-inl.h:32: Assertion `(insertion_info.second)
== (true)' failed.
```
This happens because `napi_add_env_cleanup_hook` must not be called
with the same arguments multiple times unless the previously scheduled
cleanup hook with the same arguments was already executed. However,
the cleanup hook added by `napi_register_module_v1` in napi-rs on
Windows was always created with `ptr::null_mut()` as an argument.
One case where this causes a problem is when using the addon from
multiple contexts (e.g. Node.js worker threads) at the same
time. However, Node.js doesn't provide any guarantees that the N-API
addon initialisation code will run only once even per thread and
context. In fact, it's totally valid to run `process.dlopen()`
multiple times from JavaScript land in Node.js, and this will lead to
the initialisation code being run multiple times as different
`exports` objects may need to be populated. This may happen in
numerous cases, e.g.:
- When it's not possible or not desirable to use `require()` and users
must resort to using `process.dlopen()` (one use case is passing
non-default flags to `dlopen(3)`, another is ES modules). Caching
the results of `process.dlopen()` to avoid running it more than once
may not always be possible reliably in all cases (for example,
because of Jest sandbox).
- When the `require` cache is cleared.
- On Windows: `require("./addon.node")` and then
`require(path.toNamespacedPath("./addon.node"))`.
Another issue is fixed inside `napi::tokio_runtime::drop_runtime`:
there's no need to call `napi_remove_env_cleanup_hook` (it's only
useful to cancel the hooks that haven't been executed yet). Null
pointer retrieved from `arg` was being passed as the `env` argument of
that function, so it didn't do anything and just returned
`napi_invalid_arg`.
This patch makes `napi_register_module_v1` use a counter as the
cleanup hook argument, so that the value is always different. An
alternative might have been to use a higher-level abstraction around
`sys::napi_env_cleanup_hook` that would take ownership of a boxed
closure, if there is something like this in the API already. Another
alternative could have been to heap-allocate a value so that we would
have a unique valid memory address.
The patch also contains a minor code cleanup related to
`RT_REFERENCE_COUNT` along the way: the counter is encapsulated inside
its module and `ensure_runtime` takes care of incrementing it, and
less strict memory ordering is now used as there's no need for
`SeqCst` here. If desired, it can be further optimised to
`Ordering::Release` and a separate acquire fence inside the if
statement in `drop_runtime`, as `AcqRel` for every decrement is also a
bit stricter than necessary (although simpler). These changes are not
necessary to fix the issue and can be extracted to a separate patch.
At first it was tempting to use the loaded value of
`RT_REFERENCE_COUNT` as the argument for the cleanup hook but it would
have been wrong: a simple counterexample is the following sequence:
1. init in the first context (queue: 0)
2. init in the second context (queue: 0, 1)
3. destroy the first context (queue: 1)
4. init in the third context (queue: 1, 1)
* test(napi): unload test was excluded unexpected
---------
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-04-09 00:08:48 +09:00
|
|
|
|
|
|
|
RT_REFERENCE_COUNT.fetch_add(1, Ordering::Relaxed);
|
2023-03-28 13:03:00 +09:00
|
|
|
}
|
|
|
|
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(not(any(target_os = "macos", target_family = "wasm")))]
|
fix: prevent crashing when napi_register_module_v1 is called twice (#1554)
* fix: prevent crashing when napi_register_module_v1 is called twice
Currently napi-rs addons can lead to the Node.js process aborting with
the following error when initialising the addon on Windows:
```
c:\ws\src\cleanup_queue-inl.h:32: Assertion `(insertion_info.second)
== (true)' failed.
```
This happens because `napi_add_env_cleanup_hook` must not be called
with the same arguments multiple times unless the previously scheduled
cleanup hook with the same arguments was already executed. However,
the cleanup hook added by `napi_register_module_v1` in napi-rs on
Windows was always created with `ptr::null_mut()` as an argument.
One case where this causes a problem is when using the addon from
multiple contexts (e.g. Node.js worker threads) at the same
time. However, Node.js doesn't provide any guarantees that the N-API
addon initialisation code will run only once even per thread and
context. In fact, it's totally valid to run `process.dlopen()`
multiple times from JavaScript land in Node.js, and this will lead to
the initialisation code being run multiple times as different
`exports` objects may need to be populated. This may happen in
numerous cases, e.g.:
- When it's not possible or not desirable to use `require()` and users
must resort to using `process.dlopen()` (one use case is passing
non-default flags to `dlopen(3)`, another is ES modules). Caching
the results of `process.dlopen()` to avoid running it more than once
may not always be possible reliably in all cases (for example,
because of Jest sandbox).
- When the `require` cache is cleared.
- On Windows: `require("./addon.node")` and then
`require(path.toNamespacedPath("./addon.node"))`.
Another issue is fixed inside `napi::tokio_runtime::drop_runtime`:
there's no need to call `napi_remove_env_cleanup_hook` (it's only
useful to cancel the hooks that haven't been executed yet). Null
pointer retrieved from `arg` was being passed as the `env` argument of
that function, so it didn't do anything and just returned
`napi_invalid_arg`.
This patch makes `napi_register_module_v1` use a counter as the
cleanup hook argument, so that the value is always different. An
alternative might have been to use a higher-level abstraction around
`sys::napi_env_cleanup_hook` that would take ownership of a boxed
closure, if there is something like this in the API already. Another
alternative could have been to heap-allocate a value so that we would
have a unique valid memory address.
The patch also contains a minor code cleanup related to
`RT_REFERENCE_COUNT` along the way: the counter is encapsulated inside
its module and `ensure_runtime` takes care of incrementing it, and
less strict memory ordering is now used as there's no need for
`SeqCst` here. If desired, it can be further optimised to
`Ordering::Release` and a separate acquire fence inside the if
statement in `drop_runtime`, as `AcqRel` for every decrement is also a
bit stricter than necessary (although simpler). These changes are not
necessary to fix the issue and can be extracted to a separate patch.
At first it was tempting to use the loaded value of
`RT_REFERENCE_COUNT` as the argument for the cleanup hook but it would
have been wrong: a simple counterexample is the following sequence:
1. init in the first context (queue: 0)
2. init in the second context (queue: 0, 1)
3. destroy the first context (queue: 1)
4. init in the third context (queue: 1, 1)
* test(napi): unload test was excluded unexpected
---------
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-04-09 00:08:48 +09:00
|
|
|
pub(crate) unsafe extern "C" fn drop_runtime(_arg: *mut std::ffi::c_void) {
|
2022-12-16 21:07:22 +09:00
|
|
|
use std::sync::atomic::Ordering;
|
|
|
|
|
fix: prevent crashing when napi_register_module_v1 is called twice (#1554)
* fix: prevent crashing when napi_register_module_v1 is called twice
Currently napi-rs addons can lead to the Node.js process aborting with
the following error when initialising the addon on Windows:
```
c:\ws\src\cleanup_queue-inl.h:32: Assertion `(insertion_info.second)
== (true)' failed.
```
This happens because `napi_add_env_cleanup_hook` must not be called
with the same arguments multiple times unless the previously scheduled
cleanup hook with the same arguments was already executed. However,
the cleanup hook added by `napi_register_module_v1` in napi-rs on
Windows was always created with `ptr::null_mut()` as an argument.
One case where this causes a problem is when using the addon from
multiple contexts (e.g. Node.js worker threads) at the same
time. However, Node.js doesn't provide any guarantees that the N-API
addon initialisation code will run only once even per thread and
context. In fact, it's totally valid to run `process.dlopen()`
multiple times from JavaScript land in Node.js, and this will lead to
the initialisation code being run multiple times as different
`exports` objects may need to be populated. This may happen in
numerous cases, e.g.:
- When it's not possible or not desirable to use `require()` and users
must resort to using `process.dlopen()` (one use case is passing
non-default flags to `dlopen(3)`, another is ES modules). Caching
the results of `process.dlopen()` to avoid running it more than once
may not always be possible reliably in all cases (for example,
because of Jest sandbox).
- When the `require` cache is cleared.
- On Windows: `require("./addon.node")` and then
`require(path.toNamespacedPath("./addon.node"))`.
Another issue is fixed inside `napi::tokio_runtime::drop_runtime`:
there's no need to call `napi_remove_env_cleanup_hook` (it's only
useful to cancel the hooks that haven't been executed yet). Null
pointer retrieved from `arg` was being passed as the `env` argument of
that function, so it didn't do anything and just returned
`napi_invalid_arg`.
This patch makes `napi_register_module_v1` use a counter as the
cleanup hook argument, so that the value is always different. An
alternative might have been to use a higher-level abstraction around
`sys::napi_env_cleanup_hook` that would take ownership of a boxed
closure, if there is something like this in the API already. Another
alternative could have been to heap-allocate a value so that we would
have a unique valid memory address.
The patch also contains a minor code cleanup related to
`RT_REFERENCE_COUNT` along the way: the counter is encapsulated inside
its module and `ensure_runtime` takes care of incrementing it, and
less strict memory ordering is now used as there's no need for
`SeqCst` here. If desired, it can be further optimised to
`Ordering::Release` and a separate acquire fence inside the if
statement in `drop_runtime`, as `AcqRel` for every decrement is also a
bit stricter than necessary (although simpler). These changes are not
necessary to fix the issue and can be extracted to a separate patch.
At first it was tempting to use the loaded value of
`RT_REFERENCE_COUNT` as the argument for the cleanup hook but it would
have been wrong: a simple counterexample is the following sequence:
1. init in the first context (queue: 0)
2. init in the second context (queue: 0, 1)
3. destroy the first context (queue: 1)
4. init in the third context (queue: 1, 1)
* test(napi): unload test was excluded unexpected
---------
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-04-09 00:08:48 +09:00
|
|
|
if RT_REFERENCE_COUNT.fetch_sub(1, Ordering::AcqRel) == 1 {
|
2023-03-28 13:03:00 +09:00
|
|
|
RT.write().unwrap().take();
|
2022-12-16 21:07:22 +09:00
|
|
|
}
|
2022-12-16 15:35:30 +09:00
|
|
|
}
|
|
|
|
|
2022-11-15 12:50:25 +09:00
|
|
|
/// Spawns a future onto the Tokio runtime.
|
|
|
|
///
|
|
|
|
/// Depending on where you use it, you should await or abort the future in your drop function.
|
|
|
|
/// To avoid undefined behavior and memory corruptions.
|
|
|
|
pub fn spawn<F>(fut: F) -> tokio::task::JoinHandle<F::Output>
|
2021-10-20 01:02:47 +09:00
|
|
|
where
|
|
|
|
F: 'static + Send + Future<Output = ()>,
|
|
|
|
{
|
2023-03-28 13:03:00 +09:00
|
|
|
RT.read().unwrap().as_ref().unwrap().spawn(fut)
|
2021-10-20 01:02:47 +09:00
|
|
|
}
|
|
|
|
|
2022-11-15 12:49:46 +09:00
|
|
|
/// Runs a future to completion
|
|
|
|
/// This is blocking, meaning that it pauses other execution until the future is complete,
|
|
|
|
/// only use it when it is absolutely necessary, in other places use async functions instead.
|
2024-01-24 18:20:31 +09:00
|
|
|
pub fn block_on<F: Future>(fut: F) -> F::Output {
|
2023-03-28 13:03:00 +09:00
|
|
|
RT.read().unwrap().as_ref().unwrap().block_on(fut)
|
2022-11-15 12:49:46 +09:00
|
|
|
}
|
|
|
|
|
2024-01-24 18:15:36 +09:00
|
|
|
/// spawn_blocking on the current Tokio runtime.
|
|
|
|
pub fn spawn_blocking<F, R>(func: F) -> tokio::task::JoinHandle<R>
|
|
|
|
where
|
|
|
|
F: FnOnce() -> R + Send + 'static,
|
|
|
|
R: Send + 'static,
|
|
|
|
{
|
|
|
|
RT.read().unwrap().as_ref().unwrap().spawn_blocking(func)
|
|
|
|
}
|
|
|
|
|
2022-08-04 01:12:35 +09:00
|
|
|
// This function's signature must be kept in sync with the one in lib.rs, otherwise napi
|
|
|
|
// will fail to compile with the `tokio_rt` feature.
|
|
|
|
|
|
|
|
/// If the feature `tokio_rt` has been enabled this will enter the runtime context and
|
|
|
|
/// then call the provided closure. Otherwise it will just call the provided closure.
|
2022-12-16 15:32:01 +09:00
|
|
|
#[inline]
|
2022-08-04 01:12:35 +09:00
|
|
|
pub fn within_runtime_if_available<F: FnOnce() -> T, T>(f: F) -> T {
|
2023-03-28 13:03:00 +09:00
|
|
|
let _rt_guard = RT.read().unwrap().as_ref().unwrap().enter();
|
2022-08-04 01:12:35 +09:00
|
|
|
f()
|
|
|
|
}
|
|
|
|
|
2023-11-06 11:58:23 +09:00
|
|
|
struct SendableResolver<
|
|
|
|
Data: 'static + Send,
|
|
|
|
R: 'static + FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>,
|
|
|
|
> {
|
|
|
|
inner: R,
|
|
|
|
_data: PhantomData<Data>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// the `SendableResolver` will be only called in the `threadsafe_function_call_js` callback
|
|
|
|
// which means it will be always called in the Node.js JavaScript thread
|
|
|
|
// so the inner function is not required to be `Send`
|
|
|
|
// but the `Send` bound is required by the `execute_tokio_future` function
|
|
|
|
unsafe impl<Data: 'static + Send, R: 'static + FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>>
|
|
|
|
Send for SendableResolver<Data, R>
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<Data: 'static + Send, R: 'static + FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>>
|
|
|
|
SendableResolver<Data, R>
|
|
|
|
{
|
|
|
|
fn new(inner: R) -> Self {
|
|
|
|
Self {
|
|
|
|
inner,
|
|
|
|
_data: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve(self, env: sys::napi_env, data: Data) -> Result<sys::napi_value> {
|
|
|
|
(self.inner)(env, data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-05 22:29:57 +09:00
|
|
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
2021-10-20 01:02:47 +09:00
|
|
|
pub fn execute_tokio_future<
|
|
|
|
Data: 'static + Send,
|
|
|
|
Fut: 'static + Send + Future<Output = Result<Data>>,
|
2023-11-06 11:58:23 +09:00
|
|
|
Resolver: 'static + FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>,
|
2021-10-20 01:02:47 +09:00
|
|
|
>(
|
|
|
|
env: sys::napi_env,
|
|
|
|
fut: Fut,
|
|
|
|
resolver: Resolver,
|
|
|
|
) -> Result<sys::napi_value> {
|
2022-10-03 14:00:48 +09:00
|
|
|
let (deferred, promise) = JsDeferred::new(env)?;
|
2021-10-20 01:02:47 +09:00
|
|
|
|
2023-11-06 11:58:23 +09:00
|
|
|
let sendable_resolver = SendableResolver::new(resolver);
|
|
|
|
|
|
|
|
let inner = async {
|
2022-10-03 14:00:48 +09:00
|
|
|
match fut.await {
|
2023-11-06 11:58:23 +09:00
|
|
|
Ok(v) => deferred.resolve(move |env| {
|
|
|
|
sendable_resolver
|
|
|
|
.resolve(env.raw(), v)
|
|
|
|
.map(|v| unsafe { JsUnknown::from_raw_unchecked(env.raw(), v) })
|
2022-10-03 14:00:48 +09:00
|
|
|
}),
|
|
|
|
Err(e) => deferred.reject(e),
|
|
|
|
}
|
2023-03-20 19:42:27 +09:00
|
|
|
};
|
|
|
|
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(not(target_family = "wasm"))]
|
2023-03-20 19:42:27 +09:00
|
|
|
spawn(inner);
|
|
|
|
|
2023-11-20 18:10:58 +09:00
|
|
|
#[cfg(target_family = "wasm")]
|
2023-11-07 02:46:43 +09:00
|
|
|
{
|
|
|
|
std::thread::spawn(|| {
|
|
|
|
block_on(inner);
|
|
|
|
});
|
|
|
|
}
|
2022-10-03 14:00:48 +09:00
|
|
|
|
|
|
|
Ok(promise.0.value)
|
2021-10-20 01:02:47 +09:00
|
|
|
}
|