fix(napi): js callback in threadsafefunction should not be Send (#2510)

This commit is contained in:
Cong-Cong Pan 2025-03-16 21:56:29 +08:00 committed by GitHub
parent 33fd45f708
commit cc9989f4ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 21 additions and 12 deletions

View File

@ -361,7 +361,7 @@ impl<
) -> Result<ThreadsafeFunction<T, Return, CallJsBackArgs, CalleeHandled, Weak, MaxQueueSize>>
where
CallJsBackArgs: 'static + JsValuesTupleIntoVec,
Callback: 'static + Send + FnMut(ThreadsafeCallContext<T>) -> Result<CallJsBackArgs>,
Callback: 'static + FnMut(ThreadsafeCallContext<T>) -> Result<CallJsBackArgs>,
{
ThreadsafeFunction::<T, Return, Args, CalleeHandled, Weak, MaxQueueSize>::create(
self.env,

View File

@ -249,7 +249,7 @@ impl<
// for more information.
pub(crate) fn create<
NewArgs: 'static + JsValuesTupleIntoVec,
R: 'static + Send + FnMut(ThreadsafeCallContext<T>) -> Result<NewArgs>,
R: 'static + FnMut(ThreadsafeCallContext<T>) -> Result<NewArgs>,
>(
env: sys::napi_env,
func: sys::napi_value,
@ -599,7 +599,7 @@ unsafe extern "C" fn thread_finalize_cb<T: 'static, V: 'static + JsValuesTupleIn
finalize_data: *mut c_void,
finalize_hint: *mut c_void,
) where
R: 'static + Send + FnMut(ThreadsafeCallContext<T>) -> Result<V>,
R: 'static + FnMut(ThreadsafeCallContext<T>) -> Result<V>,
{
let handle_option: Option<Arc<ThreadsafeFunctionHandle>> =
unsafe { sync::Weak::from_raw(finalize_data.cast()).upgrade() };
@ -628,7 +628,7 @@ unsafe extern "C" fn call_js_cb<
context: *mut c_void,
data: *mut c_void,
) where
R: 'static + Send + FnMut(ThreadsafeCallContext<T>) -> Result<V>,
R: 'static + FnMut(ThreadsafeCallContext<T>) -> Result<V>,
{
// env and/or callback can be null when shutting down
if raw_env.is_null() || js_callback.is_null() {

View File

@ -852,7 +852,7 @@ Generated by [AVA](https://avajs.dev).
export declare function testSerdeRoundtrip(data: any): any␊
export declare function threadsafeFunctionClosureCapture(func: (arg: string) => void): void␊
export declare function threadsafeFunctionClosureCapture(defaultValue: Animal, func: (arg: Animal) => void): void␊
export declare function threadsafeFunctionFatalMode(cb: ((arg: boolean) => unknown)): void␊

View File

@ -1226,9 +1226,10 @@ Napi4Test('throw error from ThreadsafeFunction', async (t) => {
Napi4Test('ThreadsafeFunction closure capture data', (t) => {
return new Promise((resolve) => {
threadsafeFunctionClosureCapture(() => {
const defaultValue = new Animal(Kind.Dog, '旺财')
threadsafeFunctionClosureCapture(defaultValue, (value) => {
resolve()
t.pass()
t.is(value, defaultValue)
})
})
})

View File

@ -815,7 +815,7 @@ export declare function testSerdeBufferBytes(obj: object): bigint
export declare function testSerdeRoundtrip(data: any): any
export declare function threadsafeFunctionClosureCapture(func: (arg: string) => void): void
export declare function threadsafeFunctionClosureCapture(defaultValue: Animal, func: (arg: Animal) => void): void
export declare function threadsafeFunctionFatalMode(cb: ((arg: boolean) => unknown)): void

View File

@ -12,7 +12,7 @@ pub fn accept_stream(
let web_readable_stream = stream.read()?;
let mut input = StreamReader::new(web_readable_stream.map(|chunk| {
chunk
.map(bytes::Bytes::from_owner)
.map(|chunk| bytes::Bytes::copy_from_slice(&chunk))
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.reason))
}));
AsyncBlockBuilder::build_with_map(

View File

@ -5,6 +5,8 @@ use napi::{
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue},
};
use crate::class::Animal;
#[napi]
pub fn call_threadsafe_function(
tsfn: Arc<ThreadsafeFunction<u32, UnknownReturnValue>>,
@ -70,13 +72,19 @@ pub fn threadsafe_function_fatal_mode_error(
}
#[napi]
fn threadsafe_function_closure_capture(func: Function<String, ()>) -> napi::Result<()> {
fn threadsafe_function_closure_capture(
env: Env,
default_value: ClassInstance<Animal>,
func: Function<Reference<Animal>, ()>,
) -> napi::Result<()> {
let str = "test";
let default_value_reference: Reference<Animal> =
unsafe { Reference::from_napi_value(env.raw(), default_value.value)? };
let tsfn = func
.build_threadsafe_function::<()>()
.build_callback(move |_| {
.build_callback(move |ctx| {
println!("Captured in ThreadsafeFunction {}", str); // str is NULL at this point
Ok(String::new())
Ok(default_value_reference.clone(ctx.env)?)
})?;
tsfn.call((), ThreadsafeFunctionCallMode::NonBlocking);