diff --git a/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs b/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs index c7363b4c..663360a3 100644 --- a/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs @@ -7,8 +7,6 @@ use std::sync::{ Arc, }; -#[cfg(feature = "napi4")] -use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_CLOSED, MAIN_THREAD_ID}; pub use crate::js_values::TypedArrayType; use crate::{check_status, sys, Error, Result, Status}; @@ -66,31 +64,6 @@ macro_rules! impl_typed_array { fn drop(&mut self) { if Arc::strong_count(&self.drop_in_vm) == 1 { if let Some((ref_, env)) = self.raw { - #[cfg(feature = "napi4")] - { - if CUSTOM_GC_TSFN_CLOSED.load(std::sync::atomic::Ordering::SeqCst) { - return; - } - if !MAIN_THREAD_ID - .get() - .map(|id| &std::thread::current().id() == id) - .unwrap_or(false) - { - let status = unsafe { - sys::napi_call_threadsafe_function( - CUSTOM_GC_TSFN.load(std::sync::atomic::Ordering::SeqCst), - ref_ as *mut c_void, - 1, - ) - }; - assert!( - status == sys::Status::napi_ok, - "Call custom GC in ArrayBuffer::drop failed {:?}", - Status::from(status) - ); - return; - } - } crate::check_status_or_throw!( env, unsafe { sys::napi_reference_unref(env, ref_, &mut 0) }, diff --git a/crates/napi/src/bindgen_runtime/js_values/bigint.rs b/crates/napi/src/bindgen_runtime/js_values/bigint.rs index 27100318..67256488 100644 --- a/crates/napi/src/bindgen_runtime/js_values/bigint.rs +++ b/crates/napi/src/bindgen_runtime/js_values/bigint.rs @@ -52,7 +52,7 @@ impl FromNapiValue for BigInt { ptr::null_mut(), ) })?; - let mut words: Vec = Vec::with_capacity(word_count as usize); + let mut words: Vec = Vec::with_capacity(word_count); let mut sign_bit = 0; unsafe { @@ -64,7 +64,7 @@ impl FromNapiValue for BigInt { words.as_mut_ptr(), ))?; - words.set_len(word_count as usize); + words.set_len(word_count); } if word_count == 0 { words = vec![0]; @@ -155,7 +155,7 @@ impl ToNapiValue for BigInt { impl ToNapiValue for i128 { unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result { let mut raw_value = ptr::null_mut(); - let sign_bit = if val > 0 { 0 } else { 1 }; + let sign_bit = i32::from(val <= 0); let words = &val as *const i128 as *const u64; check_status!(unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) diff --git a/crates/napi/src/bindgen_runtime/js_values/buffer.rs b/crates/napi/src/bindgen_runtime/js_values/buffer.rs index c39218d6..c3f98677 100644 --- a/crates/napi/src/bindgen_runtime/js_values/buffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/buffer.rs @@ -1,4 +1,4 @@ -#[cfg(debug_assertions)] +#[cfg(all(debug_assertions, not(windows)))] use std::collections::HashSet; use std::ffi::c_void; use std::mem; @@ -6,12 +6,12 @@ use std::ops::{Deref, DerefMut}; use std::ptr::{self, NonNull}; use std::slice; use std::sync::Arc; -#[cfg(debug_assertions)] +#[cfg(all(debug_assertions, not(windows)))] use std::sync::Mutex; use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType}; -#[cfg(debug_assertions)] +#[cfg(all(debug_assertions, not(windows)))] thread_local! { pub (crate) static BUFFER_DATA: Mutex> = Default::default(); } @@ -32,31 +32,6 @@ impl Drop for Buffer { fn drop(&mut self) { if Arc::strong_count(&self.ref_count) == 1 { if let Some((ref_, env)) = self.raw { - #[cfg(feature = "napi4")] - { - if CUSTOM_GC_TSFN_CLOSED.load(std::sync::atomic::Ordering::SeqCst) { - return; - } - if !MAIN_THREAD_ID - .get() - .map(|id| &std::thread::current().id() == id) - .unwrap_or(false) - { - let status = unsafe { - sys::napi_call_threadsafe_function( - CUSTOM_GC_TSFN.load(std::sync::atomic::Ordering::SeqCst), - ref_ as *mut c_void, - 1, - ) - }; - assert!( - status == sys::Status::napi_ok, - "Call custom GC in Buffer::drop failed {:?}", - Status::from(status) - ); - return; - } - } let mut ref_count = 0; check_status_or_throw!( env, @@ -94,7 +69,7 @@ impl Clone for Buffer { impl From> for Buffer { fn from(mut data: Vec) -> Self { let inner_ptr = data.as_mut_ptr(); - #[cfg(debug_assertions)] + #[cfg(all(debug_assertions, not(windows)))] { let is_existed = BUFFER_DATA.with(|buffer_data| { let buffer = buffer_data.lock().expect("Unlock buffer data failed"); diff --git a/crates/napi/src/bindgen_runtime/js_values/promise.rs b/crates/napi/src/bindgen_runtime/js_values/promise.rs index 9beea5d0..f6168d1f 100644 --- a/crates/napi/src/bindgen_runtime/js_values/promise.rs +++ b/crates/napi/src/bindgen_runtime/js_values/promise.rs @@ -77,7 +77,7 @@ impl ValidateNapiValue for Promise { } } -unsafe impl Send for Promise {} +unsafe impl Send for Promise {} impl FromNapiValue for Promise { unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result { diff --git a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs index 48a88d82..0c911680 100644 --- a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs +++ b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs @@ -6,7 +6,7 @@ use std::rc::{Rc, Weak}; use once_cell::sync::Lazy; use crate::{ - bindgen_runtime::{PersistedSingleThreadHashMap, ToNapiValue}, + bindgen_runtime::{PersistedPerInstanceHashMap, ToNapiValue}, check_status, Env, Error, Result, Status, }; @@ -16,7 +16,7 @@ type RefInformation = ( *const Cell<*mut dyn FnOnce()>, ); -pub(crate) static REFERENCE_MAP: Lazy> = +pub(crate) static REFERENCE_MAP: Lazy> = Lazy::new(Default::default); /// ### Experimental feature @@ -59,6 +59,7 @@ impl Drop for Reference { impl Reference { #[doc(hidden)] + #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn add_ref(env: crate::sys::napi_env, t: *mut c_void, value: RefInformation) { REFERENCE_MAP.borrow_mut(|map| { if let Some((_, previous_ref, previous_rc)) = map.insert(t, value) { diff --git a/crates/napi/src/bindgen_runtime/mod.rs b/crates/napi/src/bindgen_runtime/mod.rs index 436e8389..2019727b 100644 --- a/crates/napi/src/bindgen_runtime/mod.rs +++ b/crates/napi/src/bindgen_runtime/mod.rs @@ -76,7 +76,7 @@ pub unsafe extern "C" fn drop_buffer( #[allow(unused)] finalize_data: *mut c_void, finalize_hint: *mut c_void, ) { - #[cfg(debug_assertions)] + #[cfg(all(debug_assertions, not(windows)))] { js_values::BUFFER_DATA.with(|buffer_data| { let mut buffer = buffer_data.lock().expect("Unlock Buffer data failed"); diff --git a/crates/napi/src/bindgen_runtime/module_register.rs b/crates/napi/src/bindgen_runtime/module_register.rs index f0d0024e..429ebd67 100644 --- a/crates/napi/src/bindgen_runtime/module_register.rs +++ b/crates/napi/src/bindgen_runtime/module_register.rs @@ -1,21 +1,11 @@ use std::collections::{HashMap, HashSet}; -#[cfg(feature = "napi4")] -use std::ffi::c_void; use std::ffi::CStr; -use std::mem; -#[cfg(feature = "napi4")] -use std::os::raw::c_char; use std::ptr; -use std::sync::{ - atomic::{AtomicBool, AtomicPtr, Ordering}, - Mutex, -}; -#[cfg(feature = "napi4")] +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use std::thread::ThreadId; use once_cell::sync::Lazy; -#[cfg(feature = "napi4")] -use once_cell::sync::OnceCell; use crate::{ check_status, check_status_or_throw, sys, Env, JsError, JsFunction, Property, Result, Value, @@ -26,120 +16,131 @@ pub type ExportRegisterCallback = unsafe fn(sys::napi_env) -> Result Result<()>; -#[cfg(feature = "napi4")] -pub(crate) static CUSTOM_GC_TSFN: AtomicPtr = - AtomicPtr::new(ptr::null_mut()); -#[cfg(feature = "napi4")] -// CustomGC ThreadsafeFunction may be deleted during the process exit. -// And there may still some Buffer alive after that. -pub(crate) static CUSTOM_GC_TSFN_CLOSED: AtomicBool = AtomicBool::new(false); -#[cfg(feature = "napi4")] -pub(crate) static MAIN_THREAD_ID: OnceCell = OnceCell::new(); - -struct PersistedSingleThreadVec { - inner: Mutex>, +struct PersistedPerInstanceVec { + inner: AtomicPtr, + length: AtomicUsize, } -impl Default for PersistedSingleThreadVec { +impl Default for PersistedPerInstanceVec { fn default() -> Self { - PersistedSingleThreadVec { - inner: Mutex::new(Vec::new()), - } + let mut vec: Vec = Vec::with_capacity(1); + let ret = Self { + inner: AtomicPtr::new(vec.as_mut_ptr()), + length: AtomicUsize::new(0), + }; + std::mem::forget(vec); + ret } } -impl PersistedSingleThreadVec { +impl PersistedPerInstanceVec { #[allow(clippy::mut_from_ref)] fn borrow_mut(&self, f: F) where F: FnOnce(&mut [T]), { - let mut locked = self - .inner - .lock() - .expect("Acquire persisted thread vec lock failed"); - f(&mut locked); + let length = self.length.load(Ordering::Relaxed); + if length == 0 { + f(&mut []); + } else { + let inner = self.inner.load(Ordering::Relaxed); + let mut temp = 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); + std::mem::forget(temp); + } } fn push(&self, item: T) { - let mut locked = self - .inner - .lock() - .expect("Acquire persisted thread vec lock failed"); - locked.push(item); + let length = self.length.load(Ordering::Relaxed); + let inner = self.inner.load(Ordering::Relaxed); + let mut temp = 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); + } + std::mem::forget(temp); + + self.length.fetch_add(1, Ordering::Relaxed); } } -unsafe impl Send for PersistedSingleThreadVec {} -unsafe impl Sync for PersistedSingleThreadVec {} +unsafe impl Send for PersistedPerInstanceVec {} +unsafe impl Sync for PersistedPerInstanceVec {} -pub(crate) struct PersistedSingleThreadHashMap(Mutex>); +pub(crate) struct PersistedPerInstanceHashMap(*mut HashMap); + +impl PersistedPerInstanceHashMap { + pub(crate) fn from_hashmap(hashmap: HashMap) -> Self { + Self(Box::into_raw(Box::new(hashmap))) + } -impl PersistedSingleThreadHashMap { #[allow(clippy::mut_from_ref)] pub(crate) fn borrow_mut(&self, f: F) -> R where F: FnOnce(&mut HashMap) -> R, { - let mut lock = self - .0 - .lock() - .expect("Acquire persisted thread hash map lock failed"); - f(&mut *lock) + f(unsafe { Box::leak(Box::from_raw(self.0)) }) } } -impl Default for PersistedSingleThreadHashMap { +impl Default for PersistedPerInstanceHashMap { fn default() -> Self { - PersistedSingleThreadHashMap(Mutex::new(Default::default())) + let map = Default::default(); + Self(Box::into_raw(Box::new(map))) } } type ModuleRegisterCallback = - PersistedSingleThreadVec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>; + PersistedPerInstanceVec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>; -type ModuleClassProperty = PersistedSingleThreadHashMap< +type ModuleClassProperty = PersistedPerInstanceHashMap< &'static str, HashMap, (&'static str, Vec)>, >; -unsafe impl Send for PersistedSingleThreadHashMap {} -unsafe impl Sync for PersistedSingleThreadHashMap {} +unsafe impl Send for PersistedPerInstanceHashMap {} +unsafe impl Sync for PersistedPerInstanceHashMap {} type FnRegisterMap = - PersistedSingleThreadHashMap; + PersistedPerInstanceHashMap; +type RegisteredClassesMap = PersistedPerInstanceHashMap; static MODULE_REGISTER_CALLBACK: Lazy = Lazy::new(Default::default); static MODULE_CLASS_PROPERTIES: Lazy = Lazy::new(Default::default); -static MODULE_REGISTER_LOCK: Lazy> = Lazy::new(|| Mutex::new(())); -static REGISTERED: Lazy = Lazy::new(|| AtomicBool::new(false)); -static REGISTERED_CLASSES: Lazy>> = - Lazy::new(thread_local::ThreadLocal::new); +static IS_FIRST_MODULE: AtomicBool = AtomicBool::new(true); +static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false); +static REGISTERED_CLASSES: Lazy = Lazy::new(Default::default); static FN_REGISTER_MAP: Lazy = Lazy::new(Default::default); +type RegisteredClasses = + PersistedPerInstanceHashMap; + +#[cfg(feature = "compat-mode")] +// compatibility for #[module_exports] +static MODULE_EXPORTS: Lazy> = + Lazy::new(Default::default); + #[inline] fn wait_first_thread_registered() { - while !REGISTERED.load(Ordering::SeqCst) { + while !FIRST_MODULE_REGISTERED.load(Ordering::SeqCst) { std::hint::spin_loop(); } } -type RegisteredClasses = - HashMap; - -#[cfg(feature = "compat-mode")] -// compatibility for #[module_exports] - -static MODULE_EXPORTS: Lazy> = - Lazy::new(Default::default); - #[doc(hidden)] pub fn get_class_constructor(js_name: &'static str) -> Option { - wait_first_thread_registered(); - let registered_classes = REGISTERED_CLASSES.get().unwrap(); - let registered_classes = - Box::leak(unsafe { Box::from_raw(registered_classes.load(Ordering::Relaxed)) }); - registered_classes.get(js_name).copied() + let current_id = std::thread::current().id(); + REGISTERED_CLASSES.borrow_mut(|map| { + map + .get(¤t_id) + .map(|m| m.borrow_mut(|map| map.get(js_name).copied())) + })? } #[doc(hidden)] @@ -203,7 +204,6 @@ pub fn register_class( /// ``` /// pub fn get_js_function(env: &Env, raw_fn: ExportRegisterCallback) -> Result { - wait_first_thread_registered(); FN_REGISTER_MAP.borrow_mut(|inner| { inner .get(&raw_fn) @@ -257,7 +257,6 @@ pub fn get_js_function(env: &Env, raw_fn: ExportRegisterCallback) -> Result Result { - wait_first_thread_registered(); FN_REGISTER_MAP.borrow_mut(|inner| { inner .get(&raw_fn) @@ -271,20 +270,25 @@ pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result }) } +#[cfg(windows)] +#[ctor::ctor] +fn load_host() { + unsafe { + sys::setup(); + } +} + #[no_mangle] unsafe extern "C" fn napi_register_module_v1( env: sys::napi_env, exports: sys::napi_value, ) -> sys::napi_value { - #[cfg(windows)] - unsafe { - sys::setup(); + if IS_FIRST_MODULE.load(Ordering::SeqCst) { + IS_FIRST_MODULE.store(false, Ordering::SeqCst); + } else { + wait_first_thread_registered(); } crate::__private::___CALL_FROM_FACTORY.get_or_default(); - let registered_classes_ptr = REGISTERED_CLASSES.get_or_default(); - let lock = MODULE_REGISTER_LOCK - .lock() - .expect("Failed to acquire module register lock"); let mut exports_objects: HashSet = HashSet::default(); MODULE_REGISTER_CALLBACK.borrow_mut(|inner| { inner @@ -360,8 +364,7 @@ unsafe extern "C" fn napi_register_module_v1( }) }); - let mut registered_classes: RegisteredClasses = - HashMap::with_capacity(MODULE_CLASS_PROPERTIES.borrow_mut(|inner| inner.len())); + let mut registered_classes = HashMap::new(); MODULE_CLASS_PROPERTIES.borrow_mut(|inner| { inner.iter().for_each(|(rust_name, js_mods)| { @@ -447,10 +450,13 @@ unsafe extern "C" fn napi_register_module_v1( } } }); - registered_classes_ptr.store( - Box::into_raw(Box::new(registered_classes)), - Ordering::Relaxed, - ); + + REGISTERED_CLASSES.borrow_mut(|map| { + map.insert( + std::thread::current().id(), + PersistedPerInstanceHashMap::from_hashmap(registered_classes), + ) + }); }); #[cfg(feature = "compat-mode")] @@ -462,26 +468,18 @@ unsafe extern "C" fn napi_register_module_v1( }) }); - #[cfg(all(feature = "tokio_rt", feature = "napi4"))] + #[cfg(all(windows, feature = "napi4", feature = "tokio_rt"))] { - let _ = crate::tokio_runtime::RT.clone(); - crate::tokio_runtime::TOKIO_RT_REF_COUNT.fetch_add(1, Ordering::SeqCst); - assert_eq!( - unsafe { - sys::napi_add_env_cleanup_hook( - env, - Some(crate::shutdown_tokio_rt), - env as *mut std::ffi::c_void, - ) - }, - sys::Status::napi_ok - ); + crate::tokio_runtime::RT_REFERENCE_COUNT.fetch_add(1, Ordering::SeqCst); + unsafe { + sys::napi_add_env_cleanup_hook( + env, + Some(crate::tokio_runtime::drop_runtime), + ptr::null_mut(), + ) + }; } - mem::drop(lock); - - #[cfg(feature = "napi4")] - create_custom_gc(env); - REGISTERED.store(true, Ordering::SeqCst); + FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst); exports } @@ -502,99 +500,3 @@ pub(crate) unsafe extern "C" fn noop( } ptr::null_mut() } - -#[cfg(feature = "napi4")] -fn create_custom_gc(env: sys::napi_env) { - let mut custom_gc_fn = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { - sys::napi_create_function( - env, - "custom_gc".as_ptr() as *const c_char, - 9, - Some(empty), - ptr::null_mut(), - &mut custom_gc_fn, - ) - }, - "Create Custom GC Function in napi_register_module_v1 failed" - ); - let mut async_resource_name = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { - sys::napi_create_string_utf8( - env, - "CustomGC".as_ptr() as *const c_char, - 8, - &mut async_resource_name, - ) - }, - "Create async resource string in napi_register_module_v1 napi_register_module_v1" - ); - let mut custom_gc_tsfn = ptr::null_mut(); - check_status_or_throw!( - env, - unsafe { - sys::napi_create_threadsafe_function( - env, - custom_gc_fn, - ptr::null_mut(), - async_resource_name, - 0, - 1, - ptr::null_mut(), - Some(custom_gc_finalize), - ptr::null_mut(), - Some(custom_gc), - &mut custom_gc_tsfn, - ) - }, - "Create Custom GC ThreadsafeFunction in napi_register_module_v1 failed" - ); - check_status_or_throw!( - env, - 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::SeqCst); - MAIN_THREAD_ID.get_or_init(|| std::thread::current().id()); -} - -#[cfg(feature = "napi4")] -#[allow(unused)] -unsafe extern "C" fn empty(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value { - ptr::null_mut() -} - -#[cfg(feature = "napi4")] -#[allow(unused)] -unsafe extern "C" fn custom_gc_finalize( - env: sys::napi_env, - finalize_data: *mut c_void, - finalize_hint: *mut c_void, -) { - CUSTOM_GC_TSFN_CLOSED.store(true, Ordering::SeqCst); -} - -#[cfg(feature = "napi4")] -// recycle the ArrayBuffer/Buffer Reference if the ArrayBuffer/Buffer is not dropped on the main thread -extern "C" fn custom_gc( - env: sys::napi_env, - _js_callback: sys::napi_value, - _context: *mut c_void, - data: *mut c_void, -) { - let mut ref_count = 0; - check_status_or_throw!( - env, - unsafe { sys::napi_reference_unref(env, data as sys::napi_ref, &mut ref_count) }, - "Failed to unref Buffer reference in Custom GC" - ); - check_status_or_throw!( - env, - unsafe { sys::napi_delete_reference(env, data as sys::napi_ref) }, - "Failed to delete Buffer reference in Custom GC" - ); -} diff --git a/crates/napi/src/env.rs b/crates/napi/src/env.rs index 8b8568fc..7e5e473b 100644 --- a/crates/napi/src/env.rs +++ b/crates/napi/src/env.rs @@ -115,7 +115,7 @@ impl Env { #[cfg(feature = "napi6")] pub fn create_bigint_from_i128(&self, value: i128) -> Result { let mut raw_value = ptr::null_mut(); - let sign_bit = if value > 0 { 0 } else { 1 }; + let sign_bit = i32::from(value <= 0); let words = &value as *const i128 as *const u64; check_status!(unsafe { sys::napi_create_bigint_words(self.0, sign_bit, 2, words, &mut raw_value) diff --git a/crates/napi/src/js_values/bigint.rs b/crates/napi/src/js_values/bigint.rs index 245cddb1..2725c7be 100644 --- a/crates/napi/src/js_values/bigint.rs +++ b/crates/napi/src/js_values/bigint.rs @@ -204,7 +204,7 @@ impl TryFrom for u64 { impl JsBigInt { /// pub fn get_words(&mut self) -> Result<(bool, Vec)> { - let mut words: Vec = Vec::with_capacity(self.word_count as usize); + let mut words: Vec = Vec::with_capacity(self.word_count); let word_count = &mut self.word_count; let mut sign_bit = 0; check_status!(unsafe { @@ -218,7 +218,7 @@ impl JsBigInt { })?; unsafe { - words.set_len(self.word_count as usize); + words.set_len(self.word_count); }; Ok((sign_bit == 1, words)) diff --git a/crates/napi/src/js_values/string/mod.rs b/crates/napi/src/js_values/string/mod.rs index 0e5eeff8..313e46b1 100644 --- a/crates/napi/src/js_values/string/mod.rs +++ b/crates/napi/src/js_values/string/mod.rs @@ -35,7 +35,7 @@ impl JsString { check_status!(unsafe { sys::napi_get_value_string_utf8(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) })?; - Ok(length as usize) + Ok(length) } pub fn utf16_len(&self) -> Result { @@ -43,7 +43,7 @@ impl JsString { check_status!(unsafe { sys::napi_get_value_string_utf16(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) })?; - Ok(length as usize) + Ok(length) } pub fn latin1_len(&self) -> Result { @@ -51,7 +51,7 @@ impl JsString { check_status!(unsafe { sys::napi_get_value_string_latin1(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length) })?; - Ok(length as usize) + Ok(length) } pub fn into_utf8(self) -> Result { diff --git a/crates/napi/src/lib.rs b/crates/napi/src/lib.rs index 9d5de8a6..09323b75 100644 --- a/crates/napi/src/lib.rs +++ b/crates/napi/src/lib.rs @@ -109,8 +109,6 @@ pub use error::*; pub use js_values::*; pub use status::Status; pub use task::Task; -#[cfg(all(feature = "tokio_rt", feature = "napi4"))] -pub use tokio_runtime::shutdown_tokio_rt; pub use value_type::*; pub use version::NodeVersion; #[cfg(feature = "serde-json")] diff --git a/crates/napi/src/tokio_runtime.rs b/crates/napi/src/tokio_runtime.rs index 932d387d..ef923ac5 100644 --- a/crates/napi/src/tokio_runtime.rs +++ b/crates/napi/src/tokio_runtime.rs @@ -1,54 +1,33 @@ -use std::ffi::c_void; use std::future::Future; use std::ptr; -use std::sync::atomic::{AtomicUsize, Ordering}; use once_cell::sync::Lazy; -use tokio::{ - runtime::Handle, - sync::mpsc::{self, error::TrySendError}, -}; +use tokio::runtime::Runtime; use crate::{check_status, sys, JsDeferred, JsUnknown, NapiValue, Result}; -pub(crate) static RT: Lazy<(Handle, mpsc::Sender<()>)> = Lazy::new(|| { - let runtime = tokio::runtime::Runtime::new(); - let (sender, mut receiver) = mpsc::channel::<()>(1); - runtime - .map(|rt| { - let h = rt.handle(); - let handle = h.clone(); - handle.spawn(async move { - if receiver.recv().await.is_some() { - rt.shutdown_background(); - } - }); - - (handle, sender) - }) - .expect("Create tokio runtime failed") +pub(crate) static mut RT: Lazy> = Lazy::new(|| { + let runtime = tokio::runtime::Runtime::new().expect("Create tokio runtime failed"); + Some(runtime) }); -pub(crate) static TOKIO_RT_REF_COUNT: AtomicUsize = AtomicUsize::new(0); +#[cfg(windows)] +pub(crate) static RT_REFERENCE_COUNT: std::sync::atomic::AtomicUsize = + std::sync::atomic::AtomicUsize::new(0); -#[doc(hidden)] -#[inline(never)] -pub unsafe extern "C" fn shutdown_tokio_rt(arg: *mut c_void) { - if TOKIO_RT_REF_COUNT.fetch_sub(1, Ordering::SeqCst) == 0 { - let sender = &RT.1; - if let Err(e) = sender.clone().try_send(()) { - match e { - TrySendError::Closed(_) => {} - TrySendError::Full(_) => { - panic!("Send shutdown signal to tokio runtime failed, queue is full"); - } - } +#[cfg(windows)] +pub(crate) unsafe extern "C" fn drop_runtime(arg: *mut std::ffi::c_void) { + use std::sync::atomic::Ordering; + + if RT_REFERENCE_COUNT.fetch_sub(1, Ordering::SeqCst) == 1 { + if let Some(rt) = Lazy::get_mut(unsafe { &mut RT }) { + rt.take(); } } unsafe { let env: sys::napi_env = arg as *mut sys::napi_env__; - sys::napi_remove_env_cleanup_hook(env, Some(shutdown_tokio_rt), arg); + sys::napi_remove_env_cleanup_hook(env, Some(drop_runtime), arg); } } @@ -60,7 +39,7 @@ pub fn spawn(fut: F) -> tokio::task::JoinHandle where F: 'static + Send + Future, { - RT.0.spawn(fut) + unsafe { RT.as_ref() }.unwrap().spawn(fut) } /// Runs a future to completion @@ -70,7 +49,7 @@ pub fn block_on(fut: F) -> F::Output where F: 'static + Send + Future, { - RT.0.block_on(fut) + unsafe { RT.as_ref() }.unwrap().block_on(fut) } // This function's signature must be kept in sync with the one in lib.rs, otherwise napi @@ -78,8 +57,9 @@ where /// If the feature `tokio_rt` has been enabled this will enter the runtime context and /// then call the provided closure. Otherwise it will just call the provided closure. +#[inline] pub fn within_runtime_if_available T, T>(f: F) -> T { - let _rt_guard = RT.0.enter(); + let _rt_guard = unsafe { RT.as_ref() }.unwrap().enter(); f() } diff --git a/crates/sys/src/functions.rs b/crates/sys/src/functions.rs index 6d24f501..3041d1e4 100644 --- a/crates/sys/src/functions.rs +++ b/crates/sys/src/functions.rs @@ -738,7 +738,7 @@ pub use napi7::*; pub use napi8::*; #[cfg(windows)] -pub(super) unsafe fn load() -> Result<(), libloading::Error> { +pub(super) unsafe fn load() -> Result { let host = match libloading::os::windows::Library::this() { Ok(lib) => lib.into(), Err(err) => { @@ -764,5 +764,5 @@ pub(super) unsafe fn load() -> Result<(), libloading::Error> { napi8::load(&host)?; #[cfg(feature = "experimental")] experimental::load(&host)?; - Ok(()) + Ok(host) } diff --git a/crates/sys/src/lib.rs b/crates/sys/src/lib.rs index a0d12621..5cf4fa5b 100644 --- a/crates/sys/src/lib.rs +++ b/crates/sys/src/lib.rs @@ -77,28 +77,21 @@ macro_rules! generate { }; } -#[cfg(windows)] -use std::sync::Once; - mod functions; mod types; pub use functions::*; pub use types::*; -#[cfg(windows)] -static SETUP: Once = Once::new(); - /// Loads N-API symbols from host process. /// Must be called at least once before using any functions in bindings or /// they will panic. /// Safety: `env` must be a valid `napi_env` for the current thread #[cfg(windows)] #[allow(clippy::missing_safety_doc)] -pub unsafe fn setup() { - SETUP.call_once(|| { - if let Err(err) = load() { - panic!("{}", err); - } - }); +pub unsafe fn setup() -> libloading::Library { + match load() { + Err(err) => panic!("{}", err), + Ok(l) => l, + } } diff --git a/examples/napi-compat-mode/src/object.rs b/examples/napi-compat-mode/src/object.rs index a8f62ca6..bf5649d9 100644 --- a/examples/napi-compat-mode/src/object.rs +++ b/examples/napi-compat-mode/src/object.rs @@ -87,7 +87,7 @@ fn test_delete_named_property(ctx: CallContext) -> Result { fn test_get_property(ctx: CallContext) -> Result { let obj = ctx.get::(0)?; let key = ctx.get::(1)?; - obj.get_property(&key) + obj.get_property(key) } #[js_function(1)] diff --git a/examples/napi/__test__/unload.spec.js b/examples/napi/__test__/unload.spec.js new file mode 100644 index 00000000..21a32ca2 --- /dev/null +++ b/examples/napi/__test__/unload.spec.js @@ -0,0 +1,11 @@ +// use the commonjs syntax to prevent compiler from transpiling the module syntax + +const test = require('ava').default + +test('unload module', (t) => { + const { add } = require('../index.node') + t.is(add(1, 2), 3) + delete require.cache[require.resolve('../index.node')] + const { add: add2 } = require('../index.node') + t.is(add2(1, 2), 3) +}) diff --git a/examples/napi/src/either.rs b/examples/napi/src/either.rs index ab00955b..a316a603 100644 --- a/examples/napi/src/either.rs +++ b/examples/napi/src/either.rs @@ -22,13 +22,7 @@ fn either3(input: Either3) -> u32 { match input { Either3::A(s) => s.len() as u32, Either3::B(n) => n, - Either3::C(b) => { - if b { - 1 - } else { - 0 - } - } + Either3::C(b) => u32::from(b), } } @@ -42,13 +36,7 @@ fn either4(input: Either4) -> u32 { match input { Either4::A(s) => s.len() as u32, Either4::B(n) => n, - Either4::C(b) => { - if b { - 1 - } else { - 0 - } - } + Either4::C(b) => u32::from(b), Either4::D(f) => match f.v { Either::A(s) => s.len() as u32, Either::B(n) => n, diff --git a/examples/napi/src/typed_array.rs b/examples/napi/src/typed_array.rs index 9cc5ae14..2d7caaef 100644 --- a/examples/napi/src/typed_array.rs +++ b/examples/napi/src/typed_array.rs @@ -69,5 +69,5 @@ impl Task for AsyncBuffer { #[napi] fn async_reduce_buffer(buf: Buffer) -> Result> { - Ok(AsyncTask::new(AsyncBuffer { buf: buf.clone() })) + Ok(AsyncTask::new(AsyncBuffer { buf })) } diff --git a/memory-testing/src/lib.rs b/memory-testing/src/lib.rs index b41c1362..1b0c432d 100644 --- a/memory-testing/src/lib.rs +++ b/memory-testing/src/lib.rs @@ -75,7 +75,7 @@ pub fn test_async(env: Env) -> napi::Result { #[napi] pub fn from_js(env: Env, input_object: Object) -> napi::Result { - let a: Welcome = env.from_js_value(&input_object)?; + let a: Welcome = env.from_js_value(input_object)?; Ok(serde_json::to_string(&a)?) }