fix(napi): thread safe issue while creating class instance (#1561)
This commit is contained in:
parent
77f53a8cc9
commit
d14fdca242
5 changed files with 42 additions and 19 deletions
|
@ -1,14 +1,10 @@
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use crate::{bindgen_runtime::ToNapiValue, check_status, Env, Error, Result, Status};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
bindgen_runtime::{PersistedPerInstanceHashMap, ToNapiValue},
|
|
||||||
check_status, Env, Error, Result, Status,
|
|
||||||
};
|
|
||||||
|
|
||||||
type RefInformation = (
|
type RefInformation = (
|
||||||
*mut c_void,
|
*mut c_void,
|
||||||
|
@ -16,8 +12,9 @@ type RefInformation = (
|
||||||
*const Cell<*mut dyn FnOnce()>,
|
*const Cell<*mut dyn FnOnce()>,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) static REFERENCE_MAP: Lazy<PersistedPerInstanceHashMap<*mut c_void, RefInformation>> =
|
thread_local! {
|
||||||
Lazy::new(Default::default);
|
pub(crate) static REFERENCE_MAP: RefCell<HashMap<*mut c_void, RefInformation>> = RefCell::new(HashMap::default());
|
||||||
|
}
|
||||||
|
|
||||||
/// ### Experimental feature
|
/// ### Experimental feature
|
||||||
///
|
///
|
||||||
|
@ -61,8 +58,8 @@ impl<T: 'static> Reference<T> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
pub fn add_ref(env: crate::sys::napi_env, t: *mut c_void, value: RefInformation) {
|
pub fn add_ref(env: crate::sys::napi_env, t: *mut c_void, value: RefInformation) {
|
||||||
REFERENCE_MAP.borrow_mut(|map| {
|
REFERENCE_MAP.with(|map| {
|
||||||
if let Some((_, previous_ref, previous_rc)) = map.insert(t, value) {
|
if let Some((_, previous_ref, previous_rc)) = map.borrow_mut().insert(t, value) {
|
||||||
unsafe { Rc::from_raw(previous_rc) };
|
unsafe { Rc::from_raw(previous_rc) };
|
||||||
unsafe { crate::sys::napi_delete_reference(env, previous_ref) };
|
unsafe { crate::sys::napi_delete_reference(env, previous_ref) };
|
||||||
}
|
}
|
||||||
|
@ -72,7 +69,7 @@ impl<T: 'static> Reference<T> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub unsafe fn from_value_ptr(t: *mut c_void, env: crate::sys::napi_env) -> Result<Self> {
|
pub unsafe fn from_value_ptr(t: *mut c_void, env: crate::sys::napi_env) -> Result<Self> {
|
||||||
if let Some((wrapped_value, napi_ref, finalize_callbacks_ptr)) =
|
if let Some((wrapped_value, napi_ref, finalize_callbacks_ptr)) =
|
||||||
REFERENCE_MAP.borrow_mut(|map| map.get(&t).cloned())
|
REFERENCE_MAP.with(|map| map.borrow().get(&t).cloned())
|
||||||
{
|
{
|
||||||
check_status!(
|
check_status!(
|
||||||
unsafe { crate::sys::napi_reference_ref(env, napi_ref, &mut 0) },
|
unsafe { crate::sys::napi_reference_ref(env, napi_ref, &mut 0) },
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub unsafe extern "C" fn raw_finalize_unchecked<T: ObjectFinalize>(
|
||||||
unsafe { e.throw_into(env) };
|
unsafe { e.throw_into(env) };
|
||||||
}
|
}
|
||||||
if let Some((_, ref_val, finalize_callbacks_ptr)) =
|
if let Some((_, ref_val, finalize_callbacks_ptr)) =
|
||||||
REFERENCE_MAP.borrow_mut(|reference_map| reference_map.remove(&finalize_data))
|
REFERENCE_MAP.with(|reference_map| reference_map.borrow_mut().remove(&finalize_data))
|
||||||
{
|
{
|
||||||
let finalize_callbacks_rc = unsafe { Rc::from_raw(finalize_callbacks_ptr) };
|
let finalize_callbacks_rc = unsafe { Rc::from_raw(finalize_callbacks_ptr) };
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,7 @@ import test from 'ava'
|
||||||
|
|
||||||
import { Animal, Kind, DEFAULT_COST } from '../index'
|
import { Animal, Kind, DEFAULT_COST } from '../index'
|
||||||
|
|
||||||
const t =
|
test('should be able to require in worker thread', async (t) => {
|
||||||
process.arch === 'arm64' && process.platform === 'linux' ? test.skip : test
|
|
||||||
|
|
||||||
t('should be able to require in worker thread', async (t) => {
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from({ length: 100 }).map(() => {
|
Array.from({ length: 100 }).map(() => {
|
||||||
const w = new Worker(join(__dirname, 'worker.js'))
|
const w = new Worker(join(__dirname, 'worker.js'))
|
||||||
|
@ -30,7 +27,7 @@ t('should be able to require in worker thread', async (t) => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
t('custom GC works on worker_threads', async (t) => {
|
test('custom GC works on worker_threads', async (t) => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from({ length: 50 }).map(() =>
|
Array.from({ length: 50 }).map(() =>
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
@ -68,3 +65,25 @@ t('custom GC works on worker_threads', async (t) => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should be able to new Class in worker thread concurrently', async (t) => {
|
||||||
|
await Promise.all(
|
||||||
|
Array.from({ length: 100 }).map(() => {
|
||||||
|
const w = new Worker(join(__dirname, 'worker.js'))
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
w.postMessage({ type: 'constructor' })
|
||||||
|
w.on('message', (msg) => {
|
||||||
|
t.is(msg, 'Ellie')
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
w.on('error', (err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(() => w.terminate())
|
||||||
|
.then(() => {
|
||||||
|
t.pass()
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
|
@ -35,6 +35,13 @@ parentPort.on('message', ({ type }) => {
|
||||||
throw e
|
throw e
|
||||||
})
|
})
|
||||||
|
|
||||||
|
break
|
||||||
|
case 'constructor':
|
||||||
|
let ellie
|
||||||
|
for (let i = 0; i < 10000; i++) {
|
||||||
|
ellie = new native.Animal(native.Kind.Cat, 'Ellie')
|
||||||
|
}
|
||||||
|
parentPort.postMessage(ellie.name)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw new TypeError(`Unknown message type: ${type}`)
|
throw new TypeError(`Unknown message type: ${type}`)
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"build": "lerna run build --scope '@napi-rs/*'",
|
"build": "lerna run build --scope '@napi-rs/*'",
|
||||||
"build:bench": "yarn workspace bench build",
|
"build:bench": "yarn workspace bench build",
|
||||||
"build:memory": "yarn workspace memory-testing build",
|
"build:memory": "yarn workspace memory-testing build",
|
||||||
"build:test": "lerna run build --scope '@examples/*'",
|
"build:test": "lerna run build --stream --concurrency 1 --scope '@examples/*'",
|
||||||
"format": "run-p format:prettier format:rs format:toml",
|
"format": "run-p format:prettier format:rs format:toml",
|
||||||
"format:prettier": "prettier . -w",
|
"format:prettier": "prettier . -w",
|
||||||
"format:rs": "cargo fmt",
|
"format:rs": "cargo fmt",
|
||||||
|
|
Loading…
Reference in a new issue