fix(napi): abort threadsafe functions upon env cleanup
This fixes a deadlock/panic in Electron when the window is reloaded.
This commit is contained in:
parent
110f2196a4
commit
1006b3a489
1 changed files with 19 additions and 5 deletions
|
@ -211,9 +211,13 @@ impl<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let aborted = Arc::new(AtomicBool::new(false));
|
||||||
|
let aborted_ptr = Arc::into_raw(aborted.clone()) as *mut c_void;
|
||||||
|
check_status!(unsafe { sys::napi_add_env_cleanup_hook(env, Some(cleanup_cb), aborted_ptr) })?;
|
||||||
|
|
||||||
Ok(ThreadsafeFunction {
|
Ok(ThreadsafeFunction {
|
||||||
raw_tsfn,
|
raw_tsfn,
|
||||||
aborted: Arc::new(AtomicBool::new(false)),
|
aborted,
|
||||||
ref_count: Arc::new(AtomicUsize::new(initial_thread_count)),
|
ref_count: Arc::new(AtomicUsize::new(initial_thread_count)),
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
})
|
})
|
||||||
|
@ -321,6 +325,11 @@ impl<T: 'static, ES: ErrorStrategy::T> Drop for ThreadsafeFunction<T, ES> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn cleanup_cb(cleanup_data: *mut c_void) {
|
||||||
|
let aborted = unsafe { Arc::<AtomicBool>::from_raw(cleanup_data.cast()) };
|
||||||
|
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: NapiRaw, R>(
|
||||||
_raw_env: sys::napi_env,
|
_raw_env: sys::napi_env,
|
||||||
finalize_data: *mut c_void,
|
finalize_data: *mut c_void,
|
||||||
|
@ -341,6 +350,11 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
|
||||||
R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
|
R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<Vec<V>>,
|
||||||
ES: ErrorStrategy::T,
|
ES: ErrorStrategy::T,
|
||||||
{
|
{
|
||||||
|
// env and/or callback can be null when shutting down
|
||||||
|
if raw_env.is_null() || js_callback.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let ctx: &mut R = unsafe { &mut *context.cast::<R>() };
|
let ctx: &mut R = unsafe { &mut *context.cast::<R>() };
|
||||||
let val: Result<T> = unsafe {
|
let val: Result<T> = unsafe {
|
||||||
match ES::VALUE {
|
match ES::VALUE {
|
||||||
|
@ -410,10 +424,10 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
|
||||||
unsafe { sys::napi_get_and_clear_last_exception(raw_env, &mut error_result) },
|
unsafe { sys::napi_get_and_clear_last_exception(raw_env, &mut error_result) },
|
||||||
sys::Status::napi_ok
|
sys::Status::napi_ok
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
unsafe { sys::napi_fatal_exception(raw_env, error_result) },
|
// When shutting down, napi_fatal_exception sometimes returns another exception
|
||||||
sys::Status::napi_ok
|
let stat = unsafe { sys::napi_fatal_exception(raw_env, error_result) };
|
||||||
);
|
assert!(stat == sys::Status::napi_ok || stat == sys::Status::napi_pending_exception);
|
||||||
} else {
|
} else {
|
||||||
let error_code: Status = status.into();
|
let error_code: Status = status.into();
|
||||||
let error_code_string = format!("{:?}", error_code);
|
let error_code_string = format!("{:?}", error_code);
|
||||||
|
|
Loading…
Reference in a new issue