feat(napi): relax the value type on ThreadSafeFunction

This commit is contained in:
LongYinan 2022-04-14 00:01:32 +08:00
parent 89cce5752b
commit c553dcd4e0
4 changed files with 82 additions and 24 deletions

View file

@ -1,10 +1,14 @@
use std::any::TypeId; use std::any::TypeId;
use std::convert::TryInto; use std::convert::TryInto;
use std::ffi::CString; use std::ffi::CString;
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
use std::future::Future;
use std::mem; use std::mem;
use std::os::raw::{c_char, c_void}; use std::os::raw::{c_char, c_void};
use std::ptr; use std::ptr;
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
use crate::bindgen_runtime::ToNapiValue;
use crate::{ use crate::{
async_work::{self, AsyncWorkPromise}, async_work::{self, AsyncWorkPromise},
check_status, check_status,
@ -28,8 +32,6 @@ use crate::JsError;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
#[cfg(all(feature = "serde-json"))] #[cfg(all(feature = "serde-json"))]
use serde::Serialize; use serde::Serialize;
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
use std::future::Future;
pub type Callback = unsafe extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value; pub type Callback = unsafe extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value;
@ -1038,7 +1040,7 @@ impl Env {
#[cfg(feature = "napi4")] #[cfg(feature = "napi4")]
pub fn create_threadsafe_function< pub fn create_threadsafe_function<
T: Send, T: Send,
V: NapiRaw, V: ToNapiValue,
R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>, R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
>( >(
&self, &self,
@ -1052,7 +1054,7 @@ impl Env {
#[cfg(all(feature = "tokio_rt", feature = "napi4"))] #[cfg(all(feature = "tokio_rt", feature = "napi4"))]
pub fn execute_tokio_future< pub fn execute_tokio_future<
T: 'static + Send, T: 'static + Send,
V: 'static + NapiValue, V: 'static + ToNapiValue,
F: 'static + Send + Future<Output = Result<T>>, F: 'static + Send + Future<Output = Result<T>>,
R: 'static + Send + Sync + FnOnce(&mut Env, T) -> Result<V>, R: 'static + Send + Sync + FnOnce(&mut Env, T) -> Result<V>,
>( >(
@ -1063,7 +1065,7 @@ impl Env {
use crate::tokio_runtime; use crate::tokio_runtime;
let promise = tokio_runtime::execute_tokio_future(self.0, fut, |env, val| unsafe { let promise = tokio_runtime::execute_tokio_future(self.0, fut, |env, val| unsafe {
resolver(&mut Env::from_raw(env), val).map(|v| v.raw()) resolver(&mut Env::from_raw(env), val).and_then(|v| ToNapiValue::to_napi_value(env, v))
})?; })?;
Ok(unsafe { JsObject::from_raw_unchecked(self.0, promise) }) Ok(unsafe { JsObject::from_raw_unchecked(self.0, promise) })

View file

@ -3,7 +3,10 @@ use std::ptr;
use super::Value; use super::Value;
use crate::bindgen_runtime::TypeName; use crate::bindgen_runtime::TypeName;
#[cfg(feature = "napi4")] #[cfg(feature = "napi4")]
use crate::threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction}; use crate::{
bindgen_runtime::ToNapiValue,
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction},
};
use crate::{check_status, ValueType}; use crate::{check_status, ValueType};
use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status}; use crate::{sys, Env, Error, JsObject, JsUnknown, NapiRaw, NapiValue, Result, Status};
@ -127,7 +130,7 @@ impl JsFunction {
) -> Result<ThreadsafeFunction<T, ES>> ) -> Result<ThreadsafeFunction<T, ES>>
where where
T: 'static, T: 'static,
V: NapiRaw, V: ToNapiValue,
F: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>, F: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
ES: crate::threadsafe_function::ErrorStrategy::T, ES: crate::threadsafe_function::ErrorStrategy::T,
{ {

View file

@ -8,7 +8,8 @@ use std::ptr;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use crate::{check_status, sys, Env, Error, JsError, NapiRaw, Result, Status}; use crate::bindgen_runtime::ToNapiValue;
use crate::{check_status, sys, Env, Error, JsError, Result, Status};
/// ThreadSafeFunction Context object /// ThreadSafeFunction Context object
/// the `value` is the value passed to `call` method /// the `value` is the value passed to `call` method
@ -176,7 +177,7 @@ impl<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
/// See [napi_create_threadsafe_function](https://nodejs.org/api/n-api.html#n_api_napi_create_threadsafe_function) /// See [napi_create_threadsafe_function](https://nodejs.org/api/n-api.html#n_api_napi_create_threadsafe_function)
/// for more information. /// for more information.
pub(crate) fn create< pub(crate) fn create<
V: NapiRaw, V: ToNapiValue,
R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>, R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
>( >(
env: sys::napi_env, env: sys::napi_env,
@ -330,7 +331,7 @@ unsafe extern "C" fn cleanup_cb(cleanup_data: *mut c_void) {
aborted.store(true, Ordering::SeqCst); aborted.store(true, Ordering::SeqCst);
} }
unsafe extern "C" fn thread_finalize_cb<T: 'static, V: NapiRaw, R>( unsafe extern "C" fn thread_finalize_cb<T: 'static, V: ToNapiValue, R>(
_raw_env: sys::napi_env, _raw_env: sys::napi_env,
finalize_data: *mut c_void, finalize_data: *mut c_void,
_finalize_hint: *mut c_void, _finalize_hint: *mut c_void,
@ -341,7 +342,7 @@ unsafe extern "C" fn thread_finalize_cb<T: 'static, V: NapiRaw, R>(
drop(unsafe { Box::<R>::from_raw(finalize_data.cast()) }); drop(unsafe { Box::<R>::from_raw(finalize_data.cast()) });
} }
unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>( unsafe extern "C" fn call_js_cb<T: 'static, V: ToNapiValue, R, ES>(
raw_env: sys::napi_env, raw_env: sys::napi_env,
js_callback: sys::napi_value, js_callback: sys::napi_value,
context: *mut c_void, context: *mut c_void,
@ -378,15 +379,18 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
// If the Result is an error, pass that as the first argument. // If the Result is an error, pass that as the first argument.
let status = match ret { let status = match ret {
Ok(values) => { Ok(values) => {
let values = values.iter().map(|v| unsafe { v.raw() }); let values = values
let args: Vec<sys::napi_value> = if ES::VALUE == ErrorStrategy::CalleeHandled::VALUE { .into_iter()
.map(|v| unsafe { ToNapiValue::to_napi_value(raw_env, v) });
let args: Result<Vec<sys::napi_value>> = if ES::VALUE == ErrorStrategy::CalleeHandled::VALUE {
let mut js_null = ptr::null_mut(); let mut js_null = ptr::null_mut();
unsafe { sys::napi_get_null(raw_env, &mut js_null) }; unsafe { sys::napi_get_null(raw_env, &mut js_null) };
::core::iter::once(js_null).chain(values).collect() ::core::iter::once(Ok(js_null)).chain(values).collect()
} else { } else {
values.collect() values.collect()
}; };
unsafe { match args {
Ok(args) => unsafe {
sys::napi_call_function( sys::napi_call_function(
raw_env, raw_env,
recv, recv,
@ -395,6 +399,22 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
args.as_ptr(), args.as_ptr(),
ptr::null_mut(), ptr::null_mut(),
) )
},
Err(e) => match ES::VALUE {
ErrorStrategy::Fatal::VALUE => unsafe {
sys::napi_fatal_exception(raw_env, JsError::from(e).into_value(raw_env))
},
ErrorStrategy::CalleeHandled::VALUE => unsafe {
sys::napi_call_function(
raw_env,
recv,
js_callback,
1,
[JsError::from(e).into_value(raw_env)].as_mut_ptr(),
ptr::null_mut(),
)
},
},
} }
} }
Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => unsafe { Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => unsafe {

View file

@ -1,6 +1,10 @@
use std::env; use std::env;
use napi::bindgen_prelude::*; use napi::{
bindgen_prelude::*,
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsUnknown,
};
#[napi] #[napi]
fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) { fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) {
@ -46,3 +50,32 @@ fn read_file_content() -> Result<String> {
fn return_js_function(env: Env) -> Result<JsFunction> { fn return_js_function(env: Env) -> Result<JsFunction> {
get_js_function(&env, read_file_js_function) get_js_function(&env, read_file_js_function)
} }
#[napi(
ts_generic_types = "T",
ts_args_type = "functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void"
)]
fn callback_return_promise<T: Fn() -> Result<JsUnknown>>(
env: Env,
fn_in: T,
fn_out: JsFunction,
) -> Result<JsUnknown> {
let ret = fn_in()?;
if ret.is_promise()? {
let p = Promise::<String>::from_unknown(ret)?;
let fn_out_tsfn: ThreadsafeFunction<String> = fn_out
.create_threadsafe_function(0, |ctx: ThreadSafeCallContext<String>| Ok(vec![ctx.value]))?;
env
.execute_tokio_future(
async move {
let s = p.await;
fn_out_tsfn.call(s, ThreadsafeFunctionCallMode::NonBlocking);
Ok::<(), Error>(())
},
|env, _| env.get_undefined(),
)
.map(|v| v.into_unknown())
} else {
Ok(ret)
}
}