feat(napi): call sync functions within tokio runtime (#1242)
This commit is contained in:
parent
3d2ca94392
commit
94e8e54b38
10 changed files with 55 additions and 4 deletions
|
@ -21,10 +21,12 @@ impl TryToTokens for NapiFn {
|
|||
|
||||
let native_call = if !self.is_async {
|
||||
quote! {
|
||||
let #receiver_ret_name = {
|
||||
#receiver(#(#arg_names),*)
|
||||
};
|
||||
#ret
|
||||
napi::bindgen_prelude::within_runtime_if_available(move || {
|
||||
let #receiver_ret_name = {
|
||||
#receiver(#(#arg_names),*)
|
||||
};
|
||||
#ret
|
||||
})
|
||||
}
|
||||
} else {
|
||||
let call = if self.is_ret_result {
|
||||
|
|
|
@ -162,6 +162,16 @@ pub mod bindgen_prelude {
|
|||
assert_type_of, bindgen_runtime::*, check_status, check_status_or_throw, error, error::*, sys,
|
||||
type_of, JsError, Property, PropertyAttributes, Result, Status, Task, ValueType,
|
||||
};
|
||||
|
||||
// This function's signature must be kept in sync with the one in tokio_runtime.rs, otherwise napi
|
||||
// will fail to compile without 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.
|
||||
#[cfg(not(all(feature = "tokio_rt", feature = "napi4")))]
|
||||
pub fn within_runtime_if_available<F: FnOnce() -> T, T>(f: F) -> T {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -59,6 +59,16 @@ where
|
|||
RT.0.spawn(fut);
|
||||
}
|
||||
|
||||
// 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.
|
||||
pub fn within_runtime_if_available<F: FnOnce() -> T, T>(f: F) -> T {
|
||||
let _rt_guard = RT.0.enter();
|
||||
f()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn execute_tokio_future<
|
||||
Data: 'static + Send,
|
||||
|
|
|
@ -25,6 +25,7 @@ napi-derive = { path = "../../crates/macro", features = ["type-def"] }
|
|||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
tokio = { version = "1.20.0", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { path = "../../crates/build" }
|
||||
|
|
|
@ -179,6 +179,7 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊
|
||||
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void␊
|
||||
export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): void␊
|
||||
export function useTokioWithoutAsync(): void␊
|
||||
export function getBuffer(): Buffer␊
|
||||
export function appendBuffer(buf: Buffer): Buffer␊
|
||||
export function getEmptyBuffer(): Buffer␊
|
||||
|
|
Binary file not shown.
|
@ -103,6 +103,7 @@ import {
|
|||
receiveObjectWithClassField,
|
||||
AnotherClassForEither,
|
||||
receiveDifferentClass,
|
||||
useTokioWithoutAsync,
|
||||
} from '../'
|
||||
|
||||
test('export const', (t) => {
|
||||
|
@ -691,6 +692,12 @@ Napi4Test('await Promise in rust', async (t) => {
|
|||
t.is(result, fx + 100)
|
||||
})
|
||||
|
||||
Napi4Test('Run function which uses tokio internally but is not async', (t) => {
|
||||
useTokioWithoutAsync()
|
||||
// The prior didn't throw an exception, so it worked.
|
||||
t.assert(true)
|
||||
})
|
||||
|
||||
Napi4Test('Promise should reject raw error in rust', async (t) => {
|
||||
const fxError = new Error('What is Happy Planet')
|
||||
const err = await t.throwsAsync(() => asyncPlus100(Promise.reject(fxError)))
|
||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -169,6 +169,7 @@ export function callThreadsafeFunction(callback: (...args: any[]) => any): void
|
|||
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void
|
||||
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void
|
||||
export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): void
|
||||
export function useTokioWithoutAsync(): void
|
||||
export function getBuffer(): Buffer
|
||||
export function appendBuffer(buf: Buffer): Buffer
|
||||
export function getEmptyBuffer(): Buffer
|
||||
|
|
|
@ -39,4 +39,5 @@ mod string;
|
|||
mod symbol;
|
||||
mod task;
|
||||
mod threadsafe_function;
|
||||
mod tokio_outside_async;
|
||||
mod typed_array;
|
||||
|
|
18
examples/napi/src/tokio_outside_async.rs
Normal file
18
examples/napi/src/tokio_outside_async.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use std::time::Duration;
|
||||
use tokio::{sync::oneshot, time::Instant};
|
||||
|
||||
#[napi]
|
||||
pub fn use_tokio_without_async() {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
let handle = tokio::task::spawn(async {
|
||||
// If this panics, the test failed.
|
||||
sender.send(true).unwrap();
|
||||
});
|
||||
let start = Instant::now();
|
||||
while !handle.is_finished() {
|
||||
if start.elapsed() > Duration::from_secs(5) {
|
||||
panic!("The future never resolved.");
|
||||
}
|
||||
}
|
||||
assert_eq!(receiver.blocking_recv(), Ok(true));
|
||||
}
|
Loading…
Reference in a new issue