Skip to content

Commit

Permalink
chore(napi): remove more thread_local usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Dec 16, 2022
1 parent 5bd6c78 commit e88fbcc
Showing 1 changed file with 59 additions and 15 deletions.
74 changes: 59 additions & 15 deletions crates/napi/src/bindgen_runtime/module_register.rs
@@ -1,8 +1,9 @@
use std::collections::{HashMap, HashSet};
use std::ffi::CStr;
use std::ffi::{c_void, CStr};
use std::ptr;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use std::thread::ThreadId;

use once_cell::sync::Lazy;

Expand All @@ -20,6 +21,17 @@ struct PersistedPerInstanceVec<T> {
length: AtomicUsize,
}

impl<T> Drop for PersistedPerInstanceVec<T> {
fn drop(&mut self) {
let length = self.length.load(Ordering::Relaxed);
if length == 0 {
return;
}
let inner = self.inner.load(Ordering::Relaxed);
unsafe { Vec::from_raw_parts(inner, length, length) };
}
}

impl<T> Default for PersistedPerInstanceVec<T> {
fn default() -> Self {
let mut vec: Vec<T> = Vec::with_capacity(1);
Expand Down Expand Up @@ -74,7 +86,17 @@ unsafe impl<T: Sync> Sync for PersistedPerInstanceVec<T> {}

pub(crate) struct PersistedPerInstanceHashMap<K, V>(*mut HashMap<K, V>);

impl<K, V> Drop for PersistedPerInstanceHashMap<K, V> {
fn drop(&mut self) {
unsafe { Box::from_raw(self.0) };
}
}

impl<K, V> PersistedPerInstanceHashMap<K, V> {
pub(crate) fn from_hashmap(hashmap: HashMap<K, V>) -> Self {
Self(Box::into_raw(Box::new(hashmap)))
}

#[allow(clippy::mut_from_ref)]
pub(crate) fn borrow_mut<F, R>(&self, f: F) -> R
where
Expand Down Expand Up @@ -104,12 +126,12 @@ unsafe impl<K, V> Sync for PersistedPerInstanceHashMap<K, V> {}

type FnRegisterMap =
PersistedPerInstanceHashMap<ExportRegisterCallback, (sys::napi_callback, &'static str)>;
type RegisteredClassesMap = PersistedPerInstanceHashMap<ThreadId, RegisteredClasses>;

static MODULE_REGISTER_CALLBACK: Lazy<ModuleRegisterCallback> = Lazy::new(Default::default);
static MODULE_CLASS_PROPERTIES: Lazy<ModuleClassProperty> = Lazy::new(Default::default);
static REGISTERED: AtomicBool = AtomicBool::new(false);
static REGISTERED_CLASSES: Lazy<thread_local::ThreadLocal<AtomicPtr<RegisteredClasses>>> =
Lazy::new(thread_local::ThreadLocal::new);
static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default);
static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default);

#[ctor::dtor]
Expand All @@ -135,21 +157,22 @@ fn wait_first_thread_registered() {
}

type RegisteredClasses =
HashMap</* export name */ String, /* constructor */ sys::napi_ref>;
PersistedPerInstanceHashMap</* export name */ String, /* constructor */ sys::napi_ref>;

#[cfg(feature = "compat-mode")]
// compatibility for #[module_exports]

static MODULE_EXPORTS: Lazy<PersistedPerInstanceVec<ModuleExportsCallback>> =
Lazy::new(Default::default);

#[doc(hidden)]
pub fn get_class_constructor(js_name: &'static str) -> Option<sys::napi_ref> {
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(&current_id)
.map(|m| m.borrow_mut(|map| map.get(js_name).copied()))
})?
}

#[doc(hidden)]
Expand Down Expand Up @@ -295,7 +318,6 @@ unsafe extern "C" fn napi_register_module_v1(
exports: sys::napi_value,
) -> sys::napi_value {
crate::__private::___CALL_FROM_FACTORY.get_or_default();
let registered_classes_ptr = REGISTERED_CLASSES.get_or_default();
let mut exports_objects: HashSet<String> = HashSet::default();
MODULE_REGISTER_CALLBACK.borrow_mut(|inner| {
inner
Expand Down Expand Up @@ -371,7 +393,7 @@ unsafe extern "C" fn napi_register_module_v1(
})
});

let mut registered_classes: RegisteredClasses =
let mut registered_classes =
HashMap::with_capacity(MODULE_CLASS_PROPERTIES.borrow_mut(|inner| inner.len()));

MODULE_CLASS_PROPERTIES.borrow_mut(|inner| {
Expand Down Expand Up @@ -458,10 +480,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")]
Expand All @@ -473,10 +498,29 @@ unsafe extern "C" fn napi_register_module_v1(
})
});

#[cfg(feature = "napi3")]
{
unsafe {
sys::napi_add_env_cleanup_hook(env, Some(remove_registered_classes), env as *mut c_void)
};
}
REGISTERED.store(true, Ordering::SeqCst);
exports
}

unsafe extern "C" fn remove_registered_classes(env: *mut c_void) {
let env = env as sys::napi_env;
if let Some(registered_classes) =
REGISTERED_CLASSES.borrow_mut(|map| map.remove(&std::thread::current().id()))
{
registered_classes.borrow_mut(|map| {
map.iter().for_each(|(_, v)| {
unsafe { sys::napi_delete_reference(env, *v) };
})
});
}
}

pub(crate) unsafe extern "C" fn noop(
env: sys::napi_env,
_info: sys::napi_callback_info,
Expand Down

1 comment on commit e88fbcc

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: e88fbcc Previous: 486ce35 Ratio
noop#napi-rs 63320819 ops/sec (±0.19%) 45587425 ops/sec (±0.57%) 0.72
noop#JavaScript 593840472 ops/sec (±0.12%) 709520903 ops/sec (±0.16%) 1.19
Plus number#napi-rs 21656197 ops/sec (±0.85%) 19525504 ops/sec (±0.7%) 0.90
Plus number#JavaScript 591473382 ops/sec (±0.24%) 707909446 ops/sec (±0.12%) 1.20
Create buffer#napi-rs 397953 ops/sec (±7.63%) 377660 ops/sec (±9.54%) 0.95
Create buffer#JavaScript 1702393 ops/sec (±4.16%) 1921690 ops/sec (±5.75%) 1.13
createArray#createArrayJson 44287 ops/sec (±0.14%) 39303 ops/sec (±0.29%) 0.89
createArray#create array for loop 8251 ops/sec (±0.12%) 7528 ops/sec (±0.13%) 0.91
createArray#create array with serde trait 8407 ops/sec (±0.11%) 7501 ops/sec (±0.13%) 0.89
getArrayFromJs#get array from json string 18186 ops/sec (±0.35%) 16800 ops/sec (±0.14%) 0.92
getArrayFromJs#get array from serde 10277 ops/sec (±1.6%) 10389 ops/sec (±0.03%) 1.01
getArrayFromJs#get array with for loop 12980 ops/sec (±0.53%) 12548 ops/sec (±0.03%) 0.97
Get Set property#Get Set from native#u32 386385 ops/sec (±5.74%) 415329 ops/sec (±6.94%) 1.07
Get Set property#Get Set from JavaScript#u32 312290 ops/sec (±6.32%) 358980 ops/sec (±6.93%) 1.15
Get Set property#Get Set from native#string 349849 ops/sec (±5.4%) 372414 ops/sec (±6.48%) 1.06
Get Set property#Get Set from JavaScript#string 304350 ops/sec (±5.63%) 329996 ops/sec (±7.16%) 1.08
Async task#spawn task 34731 ops/sec (±0.7%) 36776 ops/sec (±0.82%) 1.06
Async task#ThreadSafeFunction 1427 ops/sec (±9.78%) 1207 ops/sec (±21.89%) 0.85
Async task#Tokio future to Promise 29828 ops/sec (±2.4%) 30331 ops/sec (±4.76%) 1.02
Query#query * 100 1881 ops/sec (±4.75%) 1890 ops/sec (±8.31%) 1.00
Query#query * 1 30868 ops/sec (±0.53%) 31121 ops/sec (±0.69%) 1.01

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.