Skip to content

Commit

Permalink
fix(napi): add back custom gc for Send Buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Dec 19, 2022
1 parent 328b84e commit 12d1a84
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
27 changes: 27 additions & 0 deletions crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs
Expand Up @@ -7,6 +7,8 @@ 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};

Expand Down Expand Up @@ -64,6 +66,31 @@ 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) },
Expand Down
26 changes: 26 additions & 0 deletions crates/napi/src/bindgen_runtime/js_values/buffer.rs
Expand Up @@ -9,6 +9,8 @@ use std::sync::Arc;
#[cfg(all(debug_assertions, not(windows)))]
use std::sync::Mutex;

#[cfg(feature = "napi4")]
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_CLOSED, MAIN_THREAD_ID};
use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType};

#[cfg(all(debug_assertions, not(windows)))]
Expand Down Expand Up @@ -43,6 +45,30 @@ impl Drop for Buffer {
unsafe { sys::napi_delete_reference(env, ref_) },
"Failed to delete Buffer reference in drop"
);
#[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)
);
}
}
} else {
unsafe { Vec::from_raw_parts(self.inner.as_ptr(), self.len, self.capacity) };
}
Expand Down
110 changes: 110 additions & 0 deletions crates/napi/src/bindgen_runtime/module_register.rs
Expand Up @@ -117,6 +117,16 @@ static IS_FIRST_MODULE: AtomicBool = AtomicBool::new(true);
static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false);
static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default);
static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default);
#[cfg(feature = "napi4")]
pub(crate) static CUSTOM_GC_TSFN: AtomicPtr<sys::napi_threadsafe_function__> =
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: once_cell::sync::OnceCell<ThreadId> =
once_cell::sync::OnceCell::new();

type RegisteredClasses =
PersistedPerInstanceHashMap</* export name */ String, /* constructor */ sys::napi_ref>;
Expand Down Expand Up @@ -479,6 +489,8 @@ unsafe extern "C" fn napi_register_module_v1(
)
};
}
#[cfg(feature = "napi4")]
create_custom_gc(env);
FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst);
exports
}
Expand All @@ -500,3 +512,101 @@ pub(crate) unsafe extern "C" fn noop(
}
ptr::null_mut()
}

#[cfg(feature = "napi4")]
fn create_custom_gc(env: sys::napi_env) {
use std::os::raw::c_char;

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 std::ffi::c_void,
finalize_hint: *mut std::ffi::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 std::ffi::c_void,
data: *mut std::ffi::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"
);
}

1 comment on commit 12d1a84

@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: 12d1a84 Previous: e64f1b6 Ratio
noop#napi-rs 67510052 ops/sec (±0.31%) 50332687 ops/sec (±0.89%) 0.75
noop#JavaScript 591626211 ops/sec (±0.11%) 633393633 ops/sec (±0.53%) 1.07
Plus number#napi-rs 20245247 ops/sec (±0.26%) 14825624 ops/sec (±0.81%) 0.73
Plus number#JavaScript 589476834 ops/sec (±0.16%) 625180971 ops/sec (±0.52%) 1.06
Create buffer#napi-rs 441166 ops/sec (±6.67%) 333034 ops/sec (±9.61%) 0.75
Create buffer#JavaScript 2051835 ops/sec (±4.62%) 1618124 ops/sec (±7%) 0.79
createArray#createArrayJson 44343 ops/sec (±0.12%) 33273 ops/sec (±0.77%) 0.75
createArray#create array for loop 8125 ops/sec (±0.1%) 5859 ops/sec (±0.47%) 0.72
createArray#create array with serde trait 8251 ops/sec (±0.09%) 5864 ops/sec (±0.48%) 0.71
getArrayFromJs#get array from json string 17872 ops/sec (±0.27%) 14123 ops/sec (±0.43%) 0.79
getArrayFromJs#get array from serde 10611 ops/sec (±0.07%) 7773 ops/sec (±0.64%) 0.73
getArrayFromJs#get array with for loop 12894 ops/sec (±0.06%) 9819 ops/sec (±0.61%) 0.76
Get Set property#Get Set from native#u32 400477 ops/sec (±4.5%) 352812 ops/sec (±6.76%) 0.88
Get Set property#Get Set from JavaScript#u32 339875 ops/sec (±4.54%) 294761 ops/sec (±6.87%) 0.87
Get Set property#Get Set from native#string 372849 ops/sec (±4.33%) 316435 ops/sec (±6.48%) 0.85
Get Set property#Get Set from JavaScript#string 323060 ops/sec (±4.57%) 275981 ops/sec (±6.44%) 0.85
Async task#spawn task 35677 ops/sec (±0.51%) 29223 ops/sec (±2.28%) 0.82
Async task#ThreadSafeFunction 1944 ops/sec (±1.81%) 2307 ops/sec (±5.3%) 1.19
Async task#Tokio future to Promise 30933 ops/sec (±2.87%) 28721 ops/sec (±3.34%) 0.93
Query#query * 100 1956 ops/sec (±4.92%) 1482 ops/sec (±7.58%) 0.76
Query#query * 1 30534 ops/sec (±6.86%) 25470 ops/sec (±3.38%) 0.83

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

Please sign in to comment.