fix(napi): asan caught memory safety issue
This commit is contained in:
parent
afd3395bb4
commit
0dc1ef738b
14 changed files with 205 additions and 261 deletions
|
@ -33,7 +33,7 @@ build_and_test: &BUILD_AND_TEST
|
||||||
freebsd_task:
|
freebsd_task:
|
||||||
name: FreeBSD
|
name: FreeBSD
|
||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
image: freebsd-13-1-release-amd64
|
image: freebsd-13-2-release-amd64
|
||||||
env:
|
env:
|
||||||
RUSTUP_HOME: /usr/local/rustup
|
RUSTUP_HOME: /usr/local/rustup
|
||||||
CARGO_HOME: /usr/local/cargo
|
CARGO_HOME: /usr/local/cargo
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl NapiConst {
|
||||||
}
|
}
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
|
#[cfg(all(not(test), not(feature = "noop"), not(target_os = "wasi")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #register_name() {
|
fn #register_name() {
|
||||||
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
|
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
|
||||||
|
@ -49,7 +49,7 @@ impl NapiConst {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
|
#[cfg(all(not(test), not(feature = "noop"), target_os = "wasi"))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn #register_name() {
|
unsafe extern "C" fn #register_name() {
|
||||||
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
|
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #cb_name);
|
||||||
|
|
|
@ -158,14 +158,14 @@ impl NapiEnum {
|
||||||
}
|
}
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
|
#[cfg(all(not(test), not(feature = "noop"), not(target_os = "wasi")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #register_name() {
|
fn #register_name() {
|
||||||
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
|
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
|
||||||
}
|
}
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
|
#[cfg(all(not(test), not(feature = "noop"), target_os = "wasi"))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn #register_name() {
|
extern "C" fn #register_name() {
|
||||||
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
|
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
|
||||||
|
|
|
@ -589,7 +589,7 @@ impl NapiFn {
|
||||||
|
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
|
#[cfg(all(not(test), not(feature = "noop"), not(target_os = "wasi")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #module_register_name() {
|
fn #module_register_name() {
|
||||||
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
|
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
|
||||||
|
@ -597,7 +597,7 @@ impl NapiFn {
|
||||||
|
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
|
#[cfg(all(not(test), not(feature = "noop"), target_os = "wasi"))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn #module_register_name() {
|
extern "C" fn #module_register_name() {
|
||||||
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
|
napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
|
||||||
|
|
|
@ -778,7 +778,7 @@ impl NapiStruct {
|
||||||
quote! {
|
quote! {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
|
#[cfg(all(not(test), not(feature = "noop"), not(target_os = "wasi")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #struct_register_name() {
|
fn #struct_register_name() {
|
||||||
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||||
|
@ -786,7 +786,7 @@ impl NapiStruct {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
|
#[cfg(all(not(test), not(feature = "noop"), target_os = "wasi"))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn #struct_register_name() {
|
extern "C" fn #struct_register_name() {
|
||||||
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||||
|
@ -901,13 +901,13 @@ impl NapiImpl {
|
||||||
use super::*;
|
use super::*;
|
||||||
#(#methods)*
|
#(#methods)*
|
||||||
|
|
||||||
#[cfg(all(not(test), not(feature = "noop"), not(target_arch = "wasm32")))]
|
#[cfg(all(not(test), not(feature = "noop"), not(target_os = "wasi")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #register_name() {
|
fn #register_name() {
|
||||||
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(not(test), not(feature = "noop"), target_arch = "wasm32"))]
|
#[cfg(all(not(test), not(feature = "noop"), target_os = "wasi"))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn #register_name() {
|
extern "C" fn #register_name() {
|
||||||
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
|
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
|
||||||
|
|
|
@ -151,7 +151,7 @@ pub fn module_exports(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
let register = quote! {
|
let register = quote! {
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), napi::bindgen_prelude::ctor)]
|
#[cfg_attr(not(target_os = "wasi"), napi::bindgen_prelude::ctor)]
|
||||||
fn __napi__explicit_module_register() {
|
fn __napi__explicit_module_register() {
|
||||||
unsafe fn register(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) -> napi::Result<()> {
|
unsafe fn register(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) -> napi::Result<()> {
|
||||||
use napi::{Env, JsObject, NapiValue};
|
use napi::{Env, JsObject, NapiValue};
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::sync::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, THREADS_CAN_ACCESS_ENV, THREAD_DESTROYED};
|
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_DESTROYED, THREADS_CAN_ACCESS_ENV};
|
||||||
pub use crate::js_values::TypedArrayType;
|
pub use crate::js_values::TypedArrayType;
|
||||||
use crate::{check_status, sys, Error, Result, Status};
|
use crate::{check_status, sys, Error, Result, Status};
|
||||||
|
|
||||||
|
@ -77,14 +77,17 @@ macro_rules! impl_typed_array {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if Arc::strong_count(&self.drop_in_vm) == 1 {
|
if Arc::strong_count(&self.drop_in_vm) == 1 {
|
||||||
if let Some((ref_, env)) = self.raw {
|
if let Some((ref_, env)) = self.raw {
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
if ref_.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
if CUSTOM_GC_TSFN_DESTROYED.load(Ordering::SeqCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
{
|
{
|
||||||
if THREAD_DESTROYED.with(|closed| closed.load(std::sync::atomic::Ordering::Relaxed)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !THREADS_CAN_ACCESS_ENV
|
if !THREADS_CAN_ACCESS_ENV
|
||||||
.get_or_init(Default::default)
|
.borrow_mut(|m| m.get(&std::thread::current().id()).is_some())
|
||||||
.contains(&std::thread::current().id())
|
|
||||||
{
|
{
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
sys::napi_call_threadsafe_function(
|
sys::napi_call_threadsafe_function(
|
||||||
|
@ -94,18 +97,23 @@ macro_rules! impl_typed_array {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
assert!(
|
assert!(
|
||||||
status == sys::Status::napi_ok,
|
status == sys::Status::napi_ok || status == sys::Status::napi_closing,
|
||||||
"Call custom GC in ArrayBuffer::drop failed {:?}",
|
"Call custom GC in ArrayBuffer::drop failed {}",
|
||||||
Status::from(status)
|
Status::from(status)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut ref_count = 0;
|
||||||
crate::check_status_or_throw!(
|
crate::check_status_or_throw!(
|
||||||
env,
|
env,
|
||||||
unsafe { sys::napi_reference_unref(env, ref_, &mut 0) },
|
unsafe { sys::napi_reference_unref(env, ref_, &mut ref_count) },
|
||||||
"Failed to unref ArrayBuffer reference in drop"
|
"Failed to unref ArrayBuffer reference in drop"
|
||||||
);
|
);
|
||||||
|
debug_assert!(
|
||||||
|
ref_count == 0,
|
||||||
|
"ArrayBuffer reference count in ArrayBuffer::drop is not zero"
|
||||||
|
);
|
||||||
crate::check_status_or_throw!(
|
crate::check_status_or_throw!(
|
||||||
env,
|
env,
|
||||||
unsafe { sys::napi_delete_reference(env, ref_) },
|
unsafe { sys::napi_delete_reference(env, ref_) },
|
||||||
|
@ -344,13 +352,21 @@ macro_rules! impl_typed_array {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNapiValue for $name {
|
impl ToNapiValue for $name {
|
||||||
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
|
||||||
if let Some((ref_, _)) = val.raw {
|
if let Some((ref_, _)) = val.raw {
|
||||||
let mut napi_value = std::ptr::null_mut();
|
let mut napi_value = std::ptr::null_mut();
|
||||||
check_status!(
|
check_status!(
|
||||||
unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) },
|
unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) },
|
||||||
"Failed to delete reference from Buffer"
|
"Failed to get reference from ArrayBuffer"
|
||||||
)?;
|
)?;
|
||||||
|
// fast path for ArrayBuffer::drop
|
||||||
|
if Arc::strong_count(&val.drop_in_vm) == 1 {
|
||||||
|
check_status!(
|
||||||
|
unsafe { sys::napi_delete_reference(env, ref_) },
|
||||||
|
"Failed to delete reference in ArrayBuffer::to_napi_value"
|
||||||
|
)?;
|
||||||
|
val.raw = Some((ptr::null_mut(), ptr::null_mut()));
|
||||||
|
}
|
||||||
return Ok(napi_value);
|
return Ok(napi_value);
|
||||||
}
|
}
|
||||||
let mut arraybuffer_value = ptr::null_mut();
|
let mut arraybuffer_value = ptr::null_mut();
|
||||||
|
@ -422,7 +438,7 @@ macro_rules! impl_typed_array {
|
||||||
let mut napi_value = std::ptr::null_mut();
|
let mut napi_value = std::ptr::null_mut();
|
||||||
check_status!(
|
check_status!(
|
||||||
unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) },
|
unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) },
|
||||||
"Failed to delete reference from Buffer"
|
"Failed to get reference from ArrayBuffer"
|
||||||
)?;
|
)?;
|
||||||
Ok(napi_value)
|
Ok(napi_value)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,8 +9,8 @@ use std::sync::Arc;
|
||||||
#[cfg(all(debug_assertions, not(windows)))]
|
#[cfg(all(debug_assertions, not(windows)))]
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, THREADS_CAN_ACCESS_ENV, THREAD_DESTROYED};
|
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_DESTROYED, THREADS_CAN_ACCESS_ENV};
|
||||||
use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType};
|
use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType};
|
||||||
|
|
||||||
#[cfg(all(debug_assertions, not(windows)))]
|
#[cfg(all(debug_assertions, not(windows)))]
|
||||||
|
@ -34,15 +34,16 @@ impl Drop for Buffer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if Arc::strong_count(&self.ref_count) == 1 {
|
if Arc::strong_count(&self.ref_count) == 1 {
|
||||||
if let Some((ref_, env)) = self.raw {
|
if let Some((ref_, env)) = self.raw {
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
if ref_.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
if CUSTOM_GC_TSFN_DESTROYED.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
{
|
{
|
||||||
if THREAD_DESTROYED.with(|closed| closed.load(std::sync::atomic::Ordering::Relaxed)) {
|
if !THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.get(&std::thread::current().id()).is_some()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !THREADS_CAN_ACCESS_ENV
|
|
||||||
.get_or_init(Default::default)
|
|
||||||
.contains(&std::thread::current().id())
|
|
||||||
{
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
sys::napi_call_threadsafe_function(
|
sys::napi_call_threadsafe_function(
|
||||||
CUSTOM_GC_TSFN.load(std::sync::atomic::Ordering::SeqCst),
|
CUSTOM_GC_TSFN.load(std::sync::atomic::Ordering::SeqCst),
|
||||||
|
@ -51,8 +52,8 @@ impl Drop for Buffer {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
assert!(
|
assert!(
|
||||||
status == sys::Status::napi_ok,
|
status == sys::Status::napi_ok || status == sys::Status::napi_closing,
|
||||||
"Call custom GC in ArrayBuffer::drop failed {:?}",
|
"Call custom GC in Buffer::drop failed {}",
|
||||||
Status::from(status)
|
Status::from(status)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -64,6 +65,10 @@ impl Drop for Buffer {
|
||||||
unsafe { sys::napi_reference_unref(env, ref_, &mut ref_count) },
|
unsafe { sys::napi_reference_unref(env, ref_, &mut ref_count) },
|
||||||
"Failed to unref Buffer reference in drop"
|
"Failed to unref Buffer reference in drop"
|
||||||
);
|
);
|
||||||
|
debug_assert!(
|
||||||
|
ref_count == 0,
|
||||||
|
"Buffer reference count in Buffer::drop is not zero"
|
||||||
|
);
|
||||||
check_status_or_throw!(
|
check_status_or_throw!(
|
||||||
env,
|
env,
|
||||||
unsafe { sys::napi_delete_reference(env, ref_) },
|
unsafe { sys::napi_delete_reference(env, ref_) },
|
||||||
|
@ -217,7 +222,7 @@ impl FromNapiValue for Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNapiValue for Buffer {
|
impl ToNapiValue for Buffer {
|
||||||
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
|
||||||
// From Node.js value, not from `Vec<u8>`
|
// From Node.js value, not from `Vec<u8>`
|
||||||
if let Some((ref_, _)) = val.raw {
|
if let Some((ref_, _)) = val.raw {
|
||||||
let mut buf = ptr::null_mut();
|
let mut buf = ptr::null_mut();
|
||||||
|
@ -225,6 +230,14 @@ impl ToNapiValue for Buffer {
|
||||||
unsafe { sys::napi_get_reference_value(env, ref_, &mut buf) },
|
unsafe { sys::napi_get_reference_value(env, ref_, &mut buf) },
|
||||||
"Failed to get Buffer value from reference"
|
"Failed to get Buffer value from reference"
|
||||||
)?;
|
)?;
|
||||||
|
// fast path for Buffer::drop
|
||||||
|
if Arc::strong_count(&val.ref_count) == 1 {
|
||||||
|
check_status!(
|
||||||
|
unsafe { sys::napi_delete_reference(env, ref_) },
|
||||||
|
"Failed to delete Buffer reference in Buffer::to_napi_value"
|
||||||
|
)?;
|
||||||
|
val.raw = Some((ptr::null_mut(), ptr::null_mut()));
|
||||||
|
}
|
||||||
return Ok(buf);
|
return Ok(buf);
|
||||||
}
|
}
|
||||||
let len = val.len;
|
let len = val.len;
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32"), not(feature = "noop")))]
|
|
||||||
use std::hash::Hash;
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
|
#[cfg(all(
|
||||||
|
any(target_os = "windows", target_os = "freebsd"),
|
||||||
|
feature = "napi4",
|
||||||
|
feature = "tokio_rt"
|
||||||
|
))]
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
|
||||||
|
use std::sync::RwLock;
|
||||||
use std::thread::ThreadId;
|
use std::thread::ThreadId;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
@ -18,109 +21,13 @@ pub type ExportRegisterCallback = unsafe fn(sys::napi_env) -> Result<sys::napi_v
|
||||||
pub type ModuleExportsCallback =
|
pub type ModuleExportsCallback =
|
||||||
unsafe fn(env: sys::napi_env, exports: sys::napi_value) -> Result<()>;
|
unsafe fn(env: sys::napi_env, exports: sys::napi_value) -> Result<()>;
|
||||||
|
|
||||||
struct PersistedPerInstanceVec<T> {
|
#[repr(transparent)]
|
||||||
inner: AtomicPtr<T>,
|
pub(crate) struct PersistedPerInstanceHashMap<K, V>(RwLock<HashMap<K, V>>);
|
||||||
length: AtomicUsize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for PersistedPerInstanceVec<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut vec: Vec<T> = Vec::with_capacity(1);
|
|
||||||
let ret = Self {
|
|
||||||
inner: AtomicPtr::new(vec.as_mut_ptr()),
|
|
||||||
length: AtomicUsize::new(0),
|
|
||||||
};
|
|
||||||
std::mem::forget(vec);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PersistedPerInstanceVec<T> {
|
|
||||||
#[cfg(not(feature = "noop"))]
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
|
||||||
fn borrow_mut<F>(&self, f: F)
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut [T]),
|
|
||||||
{
|
|
||||||
let length = self.length.load(Ordering::Relaxed);
|
|
||||||
if length == 0 {
|
|
||||||
f(&mut []);
|
|
||||||
} else {
|
|
||||||
let inner = self.inner.load(Ordering::Relaxed);
|
|
||||||
let mut temp =
|
|
||||||
std::mem::ManuallyDrop::new(unsafe { Vec::from_raw_parts(inner, length, length) });
|
|
||||||
f(temp.as_mut_slice());
|
|
||||||
// Inner Vec has been reallocated, so we need to update the pointer
|
|
||||||
if temp.as_mut_ptr() != inner {
|
|
||||||
self.inner.store(temp.as_mut_ptr(), Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
self.length.store(temp.len(), Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&self, item: T) {
|
|
||||||
let length = self.length.load(Ordering::Relaxed);
|
|
||||||
let inner = self.inner.load(Ordering::Relaxed);
|
|
||||||
let mut temp =
|
|
||||||
std::mem::ManuallyDrop::new(unsafe { Vec::from_raw_parts(inner, length, length) });
|
|
||||||
temp.push(item);
|
|
||||||
// Inner Vec has been reallocated, so we need to update the pointer
|
|
||||||
if temp.as_mut_ptr() != inner {
|
|
||||||
self.inner.store(temp.as_mut_ptr(), Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
self.length.fetch_add(1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for PersistedPerInstanceVec<T> {}
|
|
||||||
unsafe impl<T: Sync> Sync for PersistedPerInstanceVec<T> {}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
pub(crate) struct PersistedPerInstanceHashSet<T: 'static> {
|
|
||||||
inner: *mut HashSet<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "noop"))]
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
impl<T: 'static + PartialEq + Eq + Hash> PersistedPerInstanceHashSet<T> {
|
|
||||||
pub(crate) fn insert(&self, item: T) {
|
|
||||||
Box::leak(unsafe { Box::from_raw(self.inner) }).insert(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove(&self, item: &T) {
|
|
||||||
Box::leak(unsafe { Box::from_raw(self.inner) }).remove(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
impl<T: 'static> Deref for PersistedPerInstanceHashSet<T> {
|
|
||||||
type Target = HashSet<T>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
Box::leak(unsafe { Box::from_raw(self.inner) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
impl<T: 'static> Default for PersistedPerInstanceHashSet<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Box::leak(Box::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
unsafe impl<T: Send> Send for PersistedPerInstanceHashSet<T> {}
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
unsafe impl<T: Sync> Sync for PersistedPerInstanceHashSet<T> {}
|
|
||||||
|
|
||||||
pub(crate) struct PersistedPerInstanceHashMap<K, V>(*mut HashMap<K, V>);
|
|
||||||
|
|
||||||
impl<K, V> PersistedPerInstanceHashMap<K, V> {
|
impl<K, V> PersistedPerInstanceHashMap<K, V> {
|
||||||
#[cfg(not(feature = "noop"))]
|
#[cfg(not(feature = "noop"))]
|
||||||
pub(crate) fn from_hashmap(hashmap: HashMap<K, V>) -> Self {
|
pub(crate) fn from_hashmap(hashmap: HashMap<K, V>) -> Self {
|
||||||
Self(Box::into_raw(Box::new(hashmap)))
|
Self(RwLock::new(hashmap))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
|
@ -128,19 +35,19 @@ impl<K, V> PersistedPerInstanceHashMap<K, V> {
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut HashMap<K, V>) -> R,
|
F: FnOnce(&mut HashMap<K, V>) -> R,
|
||||||
{
|
{
|
||||||
f(unsafe { Box::leak(Box::from_raw(self.0)) })
|
let mut write_lock = self.0.write().unwrap();
|
||||||
|
f(&mut *write_lock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> Default for PersistedPerInstanceHashMap<K, V> {
|
impl<K, V> Default for PersistedPerInstanceHashMap<K, V> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let map = Default::default();
|
Self(RwLock::new(HashMap::default()))
|
||||||
Self(Box::into_raw(Box::new(map)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModuleRegisterCallback =
|
type ModuleRegisterCallback =
|
||||||
PersistedPerInstanceVec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>;
|
RwLock<Vec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>>;
|
||||||
|
|
||||||
type ModuleClassProperty = PersistedPerInstanceHashMap<
|
type ModuleClassProperty = PersistedPerInstanceHashMap<
|
||||||
&'static str,
|
&'static str,
|
||||||
|
@ -162,28 +69,23 @@ static IS_FIRST_MODULE: AtomicBool = AtomicBool::new(true);
|
||||||
static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false);
|
static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false);
|
||||||
static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default);
|
static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default);
|
||||||
static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default);
|
static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default);
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
pub(crate) static CUSTOM_GC_TSFN: AtomicPtr<sys::napi_threadsafe_function__> =
|
pub(crate) static CUSTOM_GC_TSFN: AtomicPtr<sys::napi_threadsafe_function__> =
|
||||||
AtomicPtr::new(ptr::null_mut());
|
AtomicPtr::new(ptr::null_mut());
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
thread_local! {
|
pub(crate) static CUSTOM_GC_TSFN_DESTROYED: AtomicBool = AtomicBool::new(false);
|
||||||
// CustomGC ThreadsafeFunction may be deleted during the process exit.
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
// And there may still some Buffer alive after that.
|
|
||||||
pub(crate) static THREAD_DESTROYED: AtomicBool = AtomicBool::new(false);
|
|
||||||
}
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
// Store thread id of the thread that created the CustomGC ThreadsafeFunction.
|
// Store thread id of the thread that created the CustomGC ThreadsafeFunction.
|
||||||
pub(crate) static THREADS_CAN_ACCESS_ENV: once_cell::sync::OnceCell<
|
pub(crate) static THREADS_CAN_ACCESS_ENV: once_cell::sync::Lazy<
|
||||||
PersistedPerInstanceHashSet<ThreadId>,
|
PersistedPerInstanceHashMap<ThreadId, bool>,
|
||||||
> = once_cell::sync::OnceCell::new();
|
> = once_cell::sync::Lazy::new(Default::default);
|
||||||
|
|
||||||
type RegisteredClasses =
|
type RegisteredClasses =
|
||||||
PersistedPerInstanceHashMap</* export name */ String, /* constructor */ sys::napi_ref>;
|
PersistedPerInstanceHashMap</* export name */ String, /* constructor */ sys::napi_ref>;
|
||||||
|
|
||||||
#[cfg(all(feature = "compat-mode", not(feature = "noop")))]
|
#[cfg(all(feature = "compat-mode", not(feature = "noop")))]
|
||||||
// compatibility for #[module_exports]
|
// compatibility for #[module_exports]
|
||||||
static MODULE_EXPORTS: Lazy<PersistedPerInstanceVec<ModuleExportsCallback>> =
|
static MODULE_EXPORTS: Lazy<RwLock<Vec<ModuleExportsCallback>>> = Lazy::new(Default::default);
|
||||||
Lazy::new(Default::default);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "noop"))]
|
#[cfg(not(feature = "noop"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -207,7 +109,10 @@ pub fn get_class_constructor(js_name: &'static str) -> Option<sys::napi_ref> {
|
||||||
#[cfg(all(feature = "compat-mode", not(feature = "noop")))]
|
#[cfg(all(feature = "compat-mode", not(feature = "noop")))]
|
||||||
// compatibility for #[module_exports]
|
// compatibility for #[module_exports]
|
||||||
pub fn register_module_exports(callback: ModuleExportsCallback) {
|
pub fn register_module_exports(callback: ModuleExportsCallback) {
|
||||||
MODULE_EXPORTS.push(callback);
|
MODULE_EXPORTS
|
||||||
|
.write()
|
||||||
|
.expect("Register module exports failed")
|
||||||
|
.push(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -216,7 +121,10 @@ pub fn register_module_export(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
cb: ExportRegisterCallback,
|
cb: ExportRegisterCallback,
|
||||||
) {
|
) {
|
||||||
MODULE_REGISTER_CALLBACK.push((js_mod, (name, cb)));
|
MODULE_REGISTER_CALLBACK
|
||||||
|
.write()
|
||||||
|
.expect("Register module export failed")
|
||||||
|
.push((js_mod, (name, cb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -338,7 +246,7 @@ fn load_host() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_arch = "wasm32", not(feature = "noop")))]
|
#[cfg(all(target_os = "wasi", not(feature = "noop")))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn napi_register_wasm_v1(
|
unsafe extern "C" fn napi_register_wasm_v1(
|
||||||
env: sys::napi_env,
|
env: sys::napi_env,
|
||||||
|
@ -366,8 +274,12 @@ pub unsafe extern "C" fn napi_register_module_v1(
|
||||||
wait_first_thread_registered();
|
wait_first_thread_registered();
|
||||||
}
|
}
|
||||||
let mut exports_objects: HashSet<String> = HashSet::default();
|
let mut exports_objects: HashSet<String> = HashSet::default();
|
||||||
MODULE_REGISTER_CALLBACK.borrow_mut(|inner| {
|
|
||||||
inner
|
{
|
||||||
|
let mut register_callback = MODULE_REGISTER_CALLBACK
|
||||||
|
.write()
|
||||||
|
.expect("Write MODULE_REGISTER_CALLBACK in napi_register_module_v1 failed");
|
||||||
|
register_callback
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.fold(
|
.fold(
|
||||||
HashMap::<Option<&'static str>, Vec<(&'static str, ExportRegisterCallback)>>::new(),
|
HashMap::<Option<&'static str>, Vec<(&'static str, ExportRegisterCallback)>>::new(),
|
||||||
|
@ -437,8 +349,8 @@ pub unsafe extern "C" fn napi_register_module_v1(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
let mut registered_classes = HashMap::new();
|
let mut registered_classes = HashMap::new();
|
||||||
|
|
||||||
|
@ -536,15 +448,20 @@ pub unsafe extern "C" fn napi_register_module_v1(
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "compat-mode")]
|
#[cfg(feature = "compat-mode")]
|
||||||
MODULE_EXPORTS.borrow_mut(|inner| {
|
{
|
||||||
inner.iter().for_each(|callback| unsafe {
|
let module_exports = MODULE_EXPORTS.read().expect("Read MODULE_EXPORTS failed");
|
||||||
|
module_exports.iter().for_each(|callback| unsafe {
|
||||||
if let Err(e) = callback(env, exports) {
|
if let Err(e) = callback(env, exports) {
|
||||||
JsError::from(e).throw_into(env);
|
JsError::from(e).throw_into(env);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
}
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "napi4", feature = "tokio_rt"))]
|
#[cfg(all(
|
||||||
|
any(target_os = "windows", target_os = "freebsd"),
|
||||||
|
feature = "napi4",
|
||||||
|
feature = "tokio_rt"
|
||||||
|
))]
|
||||||
{
|
{
|
||||||
crate::tokio_runtime::ensure_runtime();
|
crate::tokio_runtime::ensure_runtime();
|
||||||
|
|
||||||
|
@ -560,7 +477,7 @@ pub unsafe extern "C" fn napi_register_module_v1(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
|
||||||
create_custom_gc(env);
|
create_custom_gc(env);
|
||||||
FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst);
|
FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst);
|
||||||
exports
|
exports
|
||||||
|
@ -584,67 +501,69 @@ pub(crate) unsafe extern "C" fn noop(
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32"), not(feature = "noop")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi"), not(feature = "noop")))]
|
||||||
fn create_custom_gc(env: sys::napi_env) {
|
fn create_custom_gc(env: sys::napi_env) {
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
let mut custom_gc_fn = ptr::null_mut();
|
if !FIRST_MODULE_REGISTERED.load(Ordering::SeqCst) {
|
||||||
check_status_or_throw!(
|
let mut custom_gc_fn = ptr::null_mut();
|
||||||
env,
|
check_status_or_throw!(
|
||||||
unsafe {
|
env,
|
||||||
sys::napi_create_function(
|
unsafe {
|
||||||
env,
|
sys::napi_create_function(
|
||||||
"custom_gc".as_ptr() as *const c_char,
|
env,
|
||||||
9,
|
"custom_gc".as_ptr().cast(),
|
||||||
Some(empty),
|
9,
|
||||||
ptr::null_mut(),
|
Some(empty),
|
||||||
&mut custom_gc_fn,
|
ptr::null_mut(),
|
||||||
)
|
&mut custom_gc_fn,
|
||||||
},
|
)
|
||||||
"Create Custom GC Function in napi_register_module_v1 failed"
|
},
|
||||||
);
|
"Create Custom GC Function in napi_register_module_v1 failed"
|
||||||
let mut async_resource_name = ptr::null_mut();
|
);
|
||||||
check_status_or_throw!(
|
let mut async_resource_name = ptr::null_mut();
|
||||||
env,
|
check_status_or_throw!(
|
||||||
unsafe {
|
env,
|
||||||
sys::napi_create_string_utf8(
|
unsafe {
|
||||||
env,
|
sys::napi_create_string_utf8(
|
||||||
"CustomGC".as_ptr() as *const c_char,
|
env,
|
||||||
8,
|
"CustomGC".as_ptr() as *const c_char,
|
||||||
&mut async_resource_name,
|
8,
|
||||||
)
|
&mut async_resource_name,
|
||||||
},
|
)
|
||||||
"Create async resource string in napi_register_module_v1"
|
},
|
||||||
);
|
"Create async resource string in napi_register_module_v1"
|
||||||
let mut custom_gc_tsfn = ptr::null_mut();
|
);
|
||||||
check_status_or_throw!(
|
let mut custom_gc_tsfn = ptr::null_mut();
|
||||||
env,
|
check_status_or_throw!(
|
||||||
unsafe {
|
env,
|
||||||
sys::napi_create_threadsafe_function(
|
unsafe {
|
||||||
env,
|
sys::napi_create_threadsafe_function(
|
||||||
custom_gc_fn,
|
env,
|
||||||
ptr::null_mut(),
|
custom_gc_fn,
|
||||||
async_resource_name,
|
ptr::null_mut(),
|
||||||
0,
|
async_resource_name,
|
||||||
1,
|
0,
|
||||||
ptr::null_mut(),
|
1,
|
||||||
Some(custom_gc_finalize),
|
ptr::null_mut(),
|
||||||
ptr::null_mut(),
|
Some(custom_gc_finalize),
|
||||||
Some(custom_gc),
|
ptr::null_mut(),
|
||||||
&mut custom_gc_tsfn,
|
Some(custom_gc),
|
||||||
)
|
&mut custom_gc_tsfn,
|
||||||
},
|
)
|
||||||
"Create Custom GC ThreadsafeFunction in napi_register_module_v1 failed"
|
},
|
||||||
);
|
"Create Custom GC ThreadsafeFunction in napi_register_module_v1 failed"
|
||||||
check_status_or_throw!(
|
);
|
||||||
env,
|
check_status_or_throw!(
|
||||||
unsafe { sys::napi_unref_threadsafe_function(env, custom_gc_tsfn) },
|
env,
|
||||||
"Unref Custom GC ThreadsafeFunction in napi_register_module_v1 failed"
|
unsafe { sys::napi_unref_threadsafe_function(env, custom_gc_tsfn) },
|
||||||
);
|
"Unref Custom GC ThreadsafeFunction in napi_register_module_v1 failed"
|
||||||
CUSTOM_GC_TSFN.store(custom_gc_tsfn, Ordering::Relaxed);
|
);
|
||||||
let threads = THREADS_CAN_ACCESS_ENV.get_or_init(Default::default);
|
CUSTOM_GC_TSFN.store(custom_gc_tsfn, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
let current_thread_id = std::thread::current().id();
|
let current_thread_id = std::thread::current().id();
|
||||||
threads.insert(current_thread_id);
|
THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.insert(current_thread_id, true));
|
||||||
check_status_or_throw!(
|
check_status_or_throw!(
|
||||||
env,
|
env,
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -658,33 +577,29 @@ fn create_custom_gc(env: sys::napi_env) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32"), not(feature = "noop")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi"), not(feature = "noop")))]
|
||||||
unsafe extern "C" fn remove_thread_id(id: *mut std::ffi::c_void) {
|
unsafe extern "C" fn remove_thread_id(id: *mut std::ffi::c_void) {
|
||||||
let thread_id = unsafe { Box::from_raw(id.cast::<ThreadId>()) };
|
let thread_id = unsafe { Box::from_raw(id.cast::<ThreadId>()) };
|
||||||
THREADS_CAN_ACCESS_ENV
|
THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.insert(*thread_id, false));
|
||||||
.get_or_init(Default::default)
|
|
||||||
.remove(&*thread_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32"), not(feature = "noop")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi"), not(feature = "noop")))]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
unsafe extern "C" fn empty(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value {
|
unsafe extern "C" fn empty(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value {
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32"), not(feature = "noop")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi"), not(feature = "noop")))]
|
||||||
#[allow(unused)]
|
#[allow(unused_variables)]
|
||||||
unsafe extern "C" fn custom_gc_finalize(
|
unsafe extern "C" fn custom_gc_finalize(
|
||||||
env: sys::napi_env,
|
env: sys::napi_env,
|
||||||
finalize_data: *mut std::ffi::c_void,
|
finalize_data: *mut std::ffi::c_void,
|
||||||
finalize_hint: *mut std::ffi::c_void,
|
finalize_hint: *mut std::ffi::c_void,
|
||||||
) {
|
) {
|
||||||
THREAD_DESTROYED.with(|closed| {
|
CUSTOM_GC_TSFN_DESTROYED.store(true, Ordering::SeqCst);
|
||||||
closed.store(true, Ordering::Relaxed);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32"), not(feature = "noop")))]
|
#[cfg(all(feature = "napi4", not(target_os = "wasi"), not(feature = "noop")))]
|
||||||
// recycle the ArrayBuffer/Buffer Reference if the ArrayBuffer/Buffer is not dropped on the main thread
|
// recycle the ArrayBuffer/Buffer Reference if the ArrayBuffer/Buffer is not dropped on the main thread
|
||||||
extern "C" fn custom_gc(
|
extern "C" fn custom_gc(
|
||||||
env: sys::napi_env,
|
env: sys::napi_env,
|
||||||
|
@ -692,9 +607,14 @@ extern "C" fn custom_gc(
|
||||||
_context: *mut std::ffi::c_void,
|
_context: *mut std::ffi::c_void,
|
||||||
data: *mut std::ffi::c_void,
|
data: *mut std::ffi::c_void,
|
||||||
) {
|
) {
|
||||||
|
// current thread was destroyed
|
||||||
|
if THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.get(&std::thread::current().id()) == Some(&false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut reference = 0;
|
||||||
check_status_or_throw!(
|
check_status_or_throw!(
|
||||||
env,
|
env,
|
||||||
unsafe { sys::napi_delete_reference(env, data as sys::napi_ref) },
|
unsafe { sys::napi_reference_unref(env, data.cast(), &mut reference) },
|
||||||
"Failed to delete Buffer reference in Custom GC"
|
"Failed to delete Buffer reference in Custom GC"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ use std::marker::PhantomData;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
|
|
||||||
use crate::bindgen_prelude::THREAD_DESTROYED;
|
|
||||||
use crate::bindgen_runtime::ToNapiValue;
|
use crate::bindgen_runtime::ToNapiValue;
|
||||||
use crate::{check_status, JsObject, Value};
|
use crate::{check_status, JsObject, Value};
|
||||||
use crate::{sys, Env, Error, Result};
|
use crate::{sys, Env, Error, Result};
|
||||||
|
@ -208,12 +206,6 @@ extern "C" fn napi_resolve_deferred<Data: ToNapiValue, Resolver: FnOnce(Env) ->
|
||||||
context: *mut c_void,
|
context: *mut c_void,
|
||||||
data: *mut c_void,
|
data: *mut c_void,
|
||||||
) {
|
) {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
if THREAD_DESTROYED.with(|closed| closed.load(std::sync::atomic::Ordering::Relaxed)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let deferred = context.cast();
|
let deferred = context.cast();
|
||||||
let deferred_data: Box<DeferredData<Data, Resolver>> = unsafe { Box::from_raw(data.cast()) };
|
let deferred_data: Box<DeferredData<Data, Resolver>> = unsafe { Box::from_raw(data.cast()) };
|
||||||
let result = deferred_data
|
let result = deferred_data
|
||||||
|
|
|
@ -23,14 +23,14 @@ fn create_runtime() -> Option<Runtime> {
|
||||||
|
|
||||||
pub(crate) static RT: Lazy<RwLock<Option<Runtime>>> = Lazy::new(|| RwLock::new(create_runtime()));
|
pub(crate) static RT: Lazy<RwLock<Option<Runtime>>> = Lazy::new(|| RwLock::new(create_runtime()));
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "freebsd"))]
|
||||||
static RT_REFERENCE_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
static RT_REFERENCE_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
||||||
|
|
||||||
/// Ensure that the Tokio runtime is initialized.
|
/// Ensure that the Tokio runtime is initialized.
|
||||||
/// In windows the Tokio runtime will be dropped when Node env exits.
|
/// In windows the Tokio runtime will be dropped when Node env exits.
|
||||||
/// But in Electron renderer process, the Node env will exits and recreate when the window reloads.
|
/// But in Electron renderer process, the Node env will exits and recreate when the window reloads.
|
||||||
/// So we need to ensure that the Tokio runtime is initialized when the Node env is created.
|
/// So we need to ensure that the Tokio runtime is initialized when the Node env is created.
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "freebsd"))]
|
||||||
pub(crate) fn ensure_runtime() {
|
pub(crate) fn ensure_runtime() {
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ pub(crate) fn ensure_runtime() {
|
||||||
RT_REFERENCE_COUNT.fetch_add(1, Ordering::Relaxed);
|
RT_REFERENCE_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(any(target_os = "windows", target_os = "freebsd"))]
|
||||||
pub(crate) unsafe extern "C" fn drop_runtime(_arg: *mut std::ffi::c_void) {
|
pub(crate) unsafe extern "C" fn drop_runtime(_arg: *mut std::ffi::c_void) {
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
@ -104,10 +104,10 @@ pub fn execute_tokio_future<
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_os = "wasi"))]
|
||||||
spawn(inner);
|
spawn(inner);
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_os = "wasi")]
|
||||||
block_on(inner);
|
block_on(inner);
|
||||||
|
|
||||||
Ok(promise.0.value)
|
Ok(promise.0.value)
|
||||||
|
|
|
@ -8,14 +8,19 @@ const { Animal, Kind, DEFAULT_COST } = (await import('../index.js')).default
|
||||||
|
|
||||||
const __dirname = join(fileURLToPath(import.meta.url), '..')
|
const __dirname = join(fileURLToPath(import.meta.url), '..')
|
||||||
|
|
||||||
// aarch64-unknown-linux-gnu is extremely slow in CI, skip it or it will timeout
|
|
||||||
const t =
|
const t =
|
||||||
|
// aarch64-unknown-linux-gnu is extremely slow in CI, skip it or it will timeout
|
||||||
(process.arch === 'arm64' && process.platform === 'linux') ||
|
(process.arch === 'arm64' && process.platform === 'linux') ||
|
||||||
process.env.WASI_TEST
|
process.env.WASI_TEST
|
||||||
? test.skip
|
? test.skip
|
||||||
: test
|
: test
|
||||||
|
|
||||||
const concurrency = process.platform === 'win32' || process.platform === 'darwin' || (process.platform === 'linux' && process.arch === 'x64') ? 50 : 10
|
const concurrency =
|
||||||
|
process.platform === 'win32' ||
|
||||||
|
process.platform === 'darwin' ||
|
||||||
|
(process.platform === 'linux' && process.arch === 'x64')
|
||||||
|
? 50
|
||||||
|
: 10
|
||||||
|
|
||||||
t('should be able to require in worker thread', async (t) => {
|
t('should be able to require in worker thread', async (t) => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_os = "wasi"))]
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use napi::bindgen_prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
use napi::tokio;
|
use napi::tokio;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_os = "wasi"))]
|
||||||
use napi::tokio::fs;
|
use napi::tokio::fs;
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
async fn read_file_async(path: String) -> Result<Buffer> {
|
async fn read_file_async(path: String) -> Result<Buffer> {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_os = "wasi"))]
|
||||||
{
|
{
|
||||||
fs::read(path)
|
fs::read(path)
|
||||||
.map(|r| match r {
|
.map(|r| match r {
|
||||||
|
@ -19,7 +19,7 @@ async fn read_file_async(path: String) -> Result<Buffer> {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_os = "wasi")]
|
||||||
{
|
{
|
||||||
let conetent = std::fs::read(path)?;
|
let conetent = std::fs::read(path)?;
|
||||||
Ok(conetent.into())
|
Ok(conetent.into())
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use napi::bindgen_prelude::*;
|
|
||||||
|
|
||||||
/// default enum values are continuos i32s start from 0
|
/// default enum values are continuos i32s start from 0
|
||||||
#[napi]
|
#[napi]
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
|
|
Loading…
Reference in a new issue