fix(napi): segfault when ThreadsafeFunction's callback closure captures data (#1281)

This commit is contained in:
messense 2022-08-20 22:40:26 +08:00 committed by GitHub
parent b7a3103f0c
commit 99e17c7294
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 32 additions and 5 deletions

View file

@ -214,9 +214,9 @@ impl<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
async_resource_name,
max_queue_size,
initial_thread_count,
ptr,
Some(thread_finalize_cb::<T, V, R>),
aborted_ptr as *mut c_void,
Some(thread_finalize_cb::<T, V, R>),
ptr,
Some(call_js_cb::<T, V, R, ES>),
&mut raw_tsfn,
)
@ -355,8 +355,8 @@ unsafe extern "C" fn thread_finalize_cb<T: 'static, V: ToNapiValue, R>(
R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
{
// cleanup
drop(unsafe { Box::<R>::from_raw(finalize_data.cast()) });
let aborted = unsafe { Arc::<Mutex<bool>>::from_raw(finalize_hint.cast()) };
drop(unsafe { Box::<R>::from_raw(finalize_hint.cast()) });
let aborted = unsafe { Arc::<Mutex<bool>>::from_raw(finalize_data.cast()) };
let mut is_aborted = aborted.lock().unwrap();
*is_aborted = true;
}

View file

@ -193,6 +193,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 threadsafeFunctionClosureCapture(func: (...args: any[]) => any): void␊
export function useTokioWithoutAsync(): void␊
export function getBuffer(): Buffer␊
export function appendBuffer(buf: Buffer): Buffer␊

View file

@ -50,6 +50,7 @@ import {
bigintGetU64AsString,
callThreadsafeFunction,
threadsafeFunctionThrowError,
threadsafeFunctionClosureCapture,
asyncPlus100,
getGlobal,
getUndefined,
@ -691,6 +692,15 @@ Napi4Test('throw error from thread safe function', async (t) => {
t.is(err!.message, 'ThrowFromNative')
})
Napi4Test('thread safe function closure capture data', (t) => {
return new Promise((resolve) => {
threadsafeFunctionClosureCapture(() => {
resolve()
t.pass()
})
})
})
Napi4Test('resolve value from thread safe function fatal mode', async (t) => {
const tsfnFatalMode = new Promise<boolean>((resolve) => {
threadsafeFunctionFatalMode(resolve)

View file

@ -183,6 +183,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 threadsafeFunctionClosureCapture(func: (...args: any[]) => any): void
export function useTokioWithoutAsync(): void
export function getBuffer(): Buffer
export function appendBuffer(buf: Buffer): Buffer

View file

@ -3,7 +3,7 @@ use std::thread;
use napi::{
bindgen_prelude::*,
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsBoolean,
JsBoolean, JsString,
};
#[napi]
@ -61,3 +61,18 @@ pub fn threadsafe_function_fatal_mode_error(cb: JsFunction) -> Result<()> {
});
Ok(())
}
#[napi]
fn threadsafe_function_closure_capture(func: JsFunction) -> napi::Result<()> {
let str = "test";
let tsfn: ThreadsafeFunction<()> = func
.create_threadsafe_function(0, move |_| {
println!("{}", str); // str is NULL at this point
Ok(Vec::<JsString>::new())
})
.unwrap();
tsfn.call(Ok(()), ThreadsafeFunctionCallMode::NonBlocking);
Ok(())
}