fix(napi): segfault when ThreadsafeFunction
's callback closure captures data (#1281)
This commit is contained in:
parent
b7a3103f0c
commit
99e17c7294
6 changed files with 32 additions and 5 deletions
|
@ -214,9 +214,9 @@ impl<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
|
||||||
async_resource_name,
|
async_resource_name,
|
||||||
max_queue_size,
|
max_queue_size,
|
||||||
initial_thread_count,
|
initial_thread_count,
|
||||||
ptr,
|
|
||||||
Some(thread_finalize_cb::<T, V, R>),
|
|
||||||
aborted_ptr as *mut c_void,
|
aborted_ptr as *mut c_void,
|
||||||
|
Some(thread_finalize_cb::<T, V, R>),
|
||||||
|
ptr,
|
||||||
Some(call_js_cb::<T, V, R, ES>),
|
Some(call_js_cb::<T, V, R, ES>),
|
||||||
&mut raw_tsfn,
|
&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>>,
|
R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
|
||||||
{
|
{
|
||||||
// cleanup
|
// cleanup
|
||||||
drop(unsafe { Box::<R>::from_raw(finalize_data.cast()) });
|
drop(unsafe { Box::<R>::from_raw(finalize_hint.cast()) });
|
||||||
let aborted = unsafe { Arc::<Mutex<bool>>::from_raw(finalize_hint.cast()) };
|
let aborted = unsafe { Arc::<Mutex<bool>>::from_raw(finalize_data.cast()) };
|
||||||
let mut is_aborted = aborted.lock().unwrap();
|
let mut is_aborted = aborted.lock().unwrap();
|
||||||
*is_aborted = true;
|
*is_aborted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,6 +193,7 @@ Generated by [AVA](https://avajs.dev).
|
||||||
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊
|
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊
|
||||||
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void␊
|
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void␊
|
||||||
export function threadsafeFunctionFatalModeError(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 useTokioWithoutAsync(): void␊
|
||||||
export function getBuffer(): Buffer␊
|
export function getBuffer(): Buffer␊
|
||||||
export function appendBuffer(buf: Buffer): Buffer␊
|
export function appendBuffer(buf: Buffer): Buffer␊
|
||||||
|
|
Binary file not shown.
|
@ -50,6 +50,7 @@ import {
|
||||||
bigintGetU64AsString,
|
bigintGetU64AsString,
|
||||||
callThreadsafeFunction,
|
callThreadsafeFunction,
|
||||||
threadsafeFunctionThrowError,
|
threadsafeFunctionThrowError,
|
||||||
|
threadsafeFunctionClosureCapture,
|
||||||
asyncPlus100,
|
asyncPlus100,
|
||||||
getGlobal,
|
getGlobal,
|
||||||
getUndefined,
|
getUndefined,
|
||||||
|
@ -691,6 +692,15 @@ Napi4Test('throw error from thread safe function', async (t) => {
|
||||||
t.is(err!.message, 'ThrowFromNative')
|
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) => {
|
Napi4Test('resolve value from thread safe function fatal mode', async (t) => {
|
||||||
const tsfnFatalMode = new Promise<boolean>((resolve) => {
|
const tsfnFatalMode = new Promise<boolean>((resolve) => {
|
||||||
threadsafeFunctionFatalMode(resolve)
|
threadsafeFunctionFatalMode(resolve)
|
||||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -183,6 +183,7 @@ export function callThreadsafeFunction(callback: (...args: any[]) => any): void
|
||||||
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void
|
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void
|
||||||
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void
|
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void
|
||||||
export function threadsafeFunctionFatalModeError(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 useTokioWithoutAsync(): void
|
||||||
export function getBuffer(): Buffer
|
export function getBuffer(): Buffer
|
||||||
export function appendBuffer(buf: Buffer): Buffer
|
export function appendBuffer(buf: Buffer): Buffer
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::thread;
|
||||||
use napi::{
|
use napi::{
|
||||||
bindgen_prelude::*,
|
bindgen_prelude::*,
|
||||||
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||||
JsBoolean,
|
JsBoolean, JsString,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
|
@ -61,3 +61,18 @@ pub fn threadsafe_function_fatal_mode_error(cb: JsFunction) -> Result<()> {
|
||||||
});
|
});
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue