1 | use std::collections::HashMap; |
2 | #[cfg (not(feature = "noop" ))] |
3 | use std::collections::HashSet; |
4 | use std::ffi::CStr; |
5 | use std::ptr; |
6 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" )))] |
7 | use std::sync::atomic::AtomicPtr; |
8 | #[cfg (all( |
9 | not(any(target_os = "macos" , target_family = "wasm" )), |
10 | feature = "napi4" , |
11 | feature = "tokio_rt" |
12 | ))] |
13 | use std::sync::atomic::AtomicUsize; |
14 | #[cfg (not(feature = "noop" ))] |
15 | use std::sync::atomic::{AtomicBool, Ordering}; |
16 | use std::sync::RwLock; |
17 | use std::thread::ThreadId; |
18 | |
19 | use once_cell::sync::Lazy; |
20 | |
21 | use crate::{check_status, sys, Env, JsFunction, Property, Result, Value, ValueType}; |
22 | #[cfg (not(feature = "noop" ))] |
23 | use crate::{check_status_or_throw, JsError}; |
24 | |
25 | pub type ExportRegisterCallback = unsafe fn(sys::napi_env) -> Result<sys::napi_value>; |
26 | pub type ModuleExportsCallback = |
27 | unsafe fn(env: sys::napi_env, exports: sys::napi_value) -> Result<()>; |
28 | |
29 | #[repr (transparent)] |
30 | pub(crate) struct PersistedPerInstanceHashMap<K, V>(RwLock<HashMap<K, V>>); |
31 | |
32 | impl<K, V> PersistedPerInstanceHashMap<K, V> { |
33 | #[cfg (not(feature = "noop" ))] |
34 | pub(crate) fn from_hashmap(hashmap: HashMap<K, V>) -> Self { |
35 | Self(RwLock::new(hashmap)) |
36 | } |
37 | |
38 | #[allow (clippy::mut_from_ref)] |
39 | pub(crate) fn borrow_mut<F, R>(&self, f: F) -> R |
40 | where |
41 | F: FnOnce(&mut HashMap<K, V>) -> R, |
42 | { |
43 | let mut write_lock: RwLockWriteGuard<'_, HashMap<…, …>> = self.0.write().unwrap(); |
44 | f(&mut *write_lock) |
45 | } |
46 | } |
47 | |
48 | impl<K, V> Default for PersistedPerInstanceHashMap<K, V> { |
49 | fn default() -> Self { |
50 | Self(RwLock::new(HashMap::default())) |
51 | } |
52 | } |
53 | |
54 | type ModuleRegisterCallback = |
55 | RwLock<Vec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>>; |
56 | |
57 | type ModuleClassProperty = PersistedPerInstanceHashMap< |
58 | &'static str, |
59 | HashMap<Option<&'static str>, (&'static str, Vec<Property>)>, |
60 | >; |
61 | |
62 | unsafe impl<K, V> Send for PersistedPerInstanceHashMap<K, V> {} |
63 | unsafe impl<K, V> Sync for PersistedPerInstanceHashMap<K, V> {} |
64 | |
65 | type FnRegisterMap = |
66 | PersistedPerInstanceHashMap<ExportRegisterCallback, (sys::napi_callback, &'static str)>; |
67 | type RegisteredClassesMap = PersistedPerInstanceHashMap<ThreadId, RegisteredClasses>; |
68 | |
69 | static MODULE_REGISTER_CALLBACK: Lazy<ModuleRegisterCallback> = Lazy::new(Default::default); |
70 | static MODULE_CLASS_PROPERTIES: Lazy<ModuleClassProperty> = Lazy::new(Default::default); |
71 | #[cfg (not(feature = "noop" ))] |
72 | static IS_FIRST_MODULE: AtomicBool = AtomicBool::new(true); |
73 | #[cfg (not(feature = "noop" ))] |
74 | static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false); |
75 | static REGISTERED_CLASSES: Lazy<RegisteredClassesMap> = Lazy::new(Default::default); |
76 | static FN_REGISTER_MAP: Lazy<FnRegisterMap> = Lazy::new(Default::default); |
77 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
78 | pub(crate) static CUSTOM_GC_TSFN: AtomicPtr<sys::napi_threadsafe_function__> = |
79 | AtomicPtr::new(ptr::null_mut()); |
80 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
81 | pub(crate) static CUSTOM_GC_TSFN_DESTROYED: AtomicBool = AtomicBool::new(false); |
82 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
83 | // Store thread id of the thread that created the CustomGC ThreadsafeFunction. |
84 | pub(crate) static THREADS_CAN_ACCESS_ENV: once_cell::sync::Lazy< |
85 | PersistedPerInstanceHashMap<ThreadId, bool>, |
86 | > = once_cell::sync::Lazy::new(Default::default); |
87 | |
88 | type RegisteredClasses = |
89 | PersistedPerInstanceHashMap</* export name */ String, /* constructor */ sys::napi_ref>; |
90 | |
91 | #[cfg (all(feature = "compat-mode" , not(feature = "noop" )))] |
92 | // compatibility for #[module_exports] |
93 | static MODULE_EXPORTS: Lazy<RwLock<Vec<ModuleExportsCallback>>> = Lazy::new(Default::default); |
94 | |
95 | #[cfg (not(feature = "noop" ))] |
96 | #[inline ] |
97 | fn wait_first_thread_registered() { |
98 | while !FIRST_MODULE_REGISTERED.load(order:Ordering::SeqCst) { |
99 | std::hint::spin_loop(); |
100 | } |
101 | } |
102 | |
103 | #[doc (hidden)] |
104 | pub fn get_class_constructor(js_name: &'static str) -> Option<sys::napi_ref> { |
105 | let current_id: ThreadId = std::thread::current().id(); |
106 | REGISTERED_CLASSES.borrow_mut(|map: &mut HashMap>| { |
107 | mapOption<&PersistedPerInstanceHashMap<…, …>> |
108 | .get(¤t_id) |
109 | .map(|m: &PersistedPerInstanceHashMap<…, …>| m.borrow_mut(|map: &mut HashMap| map.get(js_name).copied())) |
110 | })? |
111 | } |
112 | |
113 | #[doc (hidden)] |
114 | #[cfg (all(feature = "compat-mode" , not(feature = "noop" )))] |
115 | // compatibility for #[module_exports] |
116 | pub fn register_module_exports(callback: ModuleExportsCallback) { |
117 | MODULE_EXPORTS |
118 | .write() |
119 | .expect("Register module exports failed" ) |
120 | .push(callback); |
121 | } |
122 | |
123 | #[doc (hidden)] |
124 | pub fn register_module_export( |
125 | js_mod: Option<&'static str>, |
126 | name: &'static str, |
127 | cb: ExportRegisterCallback, |
128 | ) { |
129 | MODULE_REGISTER_CALLBACKRwLockWriteGuard<'_, Vec<…>> |
130 | .write() |
131 | .expect(msg:"Register module export failed" ) |
132 | .push((js_mod, (name, cb))); |
133 | } |
134 | |
135 | #[doc (hidden)] |
136 | pub fn register_js_function( |
137 | name: &'static str, |
138 | cb: ExportRegisterCallback, |
139 | c_fn: sys::napi_callback, |
140 | ) { |
141 | FN_REGISTER_MAP.borrow_mut(|inner: &mut HashMap …, …>| { |
142 | inner.insert(k:cb, (c_fn, name)); |
143 | }); |
144 | } |
145 | |
146 | #[doc (hidden)] |
147 | pub fn register_class( |
148 | rust_name: &'static str, |
149 | js_mod: Option<&'static str>, |
150 | js_name: &'static str, |
151 | props: Vec<Property>, |
152 | ) { |
153 | MODULE_CLASS_PROPERTIES.borrow_mut(|inner: &mut HashMap<&str, HashMap<…, …>>| { |
154 | let val: &mut HashMap = inner.entry(key:rust_name).or_default(); |
155 | let val: &mut (&str, Vec) = val.entry(key:js_mod).or_default(); |
156 | val.0 = js_name; |
157 | val.1.extend(iter:props); |
158 | }); |
159 | } |
160 | |
161 | #[inline ] |
162 | /// Get `JsFunction` from defined Rust `fn` |
163 | /// ```rust |
164 | /// #[napi] |
165 | /// fn some_fn() -> u32 { |
166 | /// 1 |
167 | /// } |
168 | /// |
169 | /// #[napi] |
170 | /// fn return_some_fn() -> Result<JsFunction> { |
171 | /// get_js_function(some_fn_js_function) |
172 | /// } |
173 | /// ``` |
174 | /// |
175 | /// ```js |
176 | /// returnSomeFn()(); // 1 |
177 | /// ``` |
178 | /// |
179 | pub fn get_js_function(env: &Env, raw_fn: ExportRegisterCallback) -> Result<JsFunction> { |
180 | FN_REGISTER_MAP.borrow_mut(|inner| { |
181 | inner |
182 | .get(&raw_fn) |
183 | .and_then(|(cb, name)| { |
184 | let mut function = ptr::null_mut(); |
185 | let name_len = name.len() - 1; |
186 | let fn_name = unsafe { CStr::from_bytes_with_nul_unchecked(name.as_bytes()) }; |
187 | check_status!(unsafe { |
188 | sys::napi_create_function( |
189 | env.0, |
190 | fn_name.as_ptr(), |
191 | name_len, |
192 | *cb, |
193 | ptr::null_mut(), |
194 | &mut function, |
195 | ) |
196 | }) |
197 | .ok()?; |
198 | Some(JsFunction(Value { |
199 | env: env.0, |
200 | value: function, |
201 | value_type: ValueType::Function, |
202 | })) |
203 | }) |
204 | .ok_or_else(|| { |
205 | crate::Error::new( |
206 | crate::Status::InvalidArg, |
207 | "JavaScript function does not exist" .to_owned(), |
208 | ) |
209 | }) |
210 | }) |
211 | } |
212 | |
213 | /// Get `C Callback` from defined Rust `fn` |
214 | /// ```rust |
215 | /// #[napi] |
216 | /// fn some_fn() -> u32 { |
217 | /// 1 |
218 | /// } |
219 | /// |
220 | /// #[napi] |
221 | /// fn create_obj(env: Env) -> Result<JsObject> { |
222 | /// let mut obj = env.create_object()?; |
223 | /// obj.define_property(&[Property::new("getter" )?.with_getter(get_c_callback(some_fn_js_function)?)])?; |
224 | /// Ok(obj) |
225 | /// } |
226 | /// ``` |
227 | /// |
228 | /// ```js |
229 | /// console.log(createObj().getter) // 1 |
230 | /// ``` |
231 | /// |
232 | pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result<crate::Callback> { |
233 | FN_REGISTER_MAP.borrow_mut(|inner: &mut HashMap …, …>| { |
234 | inner |
235 | .get(&raw_fn) |
236 | .and_then(|(cb, _name)| *cb) |
237 | .ok_or_else(|| { |
238 | crate::Error::new( |
239 | crate::Status::InvalidArg, |
240 | reason:"JavaScript function does not exist" .to_owned(), |
241 | ) |
242 | }) |
243 | }) |
244 | } |
245 | |
246 | #[cfg (all(windows, not(feature = "noop" )))] |
247 | #[ctor::ctor ] |
248 | fn load_host() { |
249 | unsafe { |
250 | sys::setup(); |
251 | } |
252 | } |
253 | |
254 | #[cfg (all(target_family = "wasm" , not(feature = "noop" )))] |
255 | #[no_mangle ] |
256 | unsafe extern "C" fn napi_register_wasm_v1( |
257 | env: sys::napi_env, |
258 | exports: sys::napi_value, |
259 | ) -> sys::napi_value { |
260 | unsafe { napi_register_module_v1(env, exports) } |
261 | } |
262 | |
263 | #[cfg (not(feature = "noop" ))] |
264 | #[no_mangle ] |
265 | /// Register the n-api module exports. |
266 | /// |
267 | /// # Safety |
268 | /// This method is meant to be called by Node.js while importing the n-api module. |
269 | /// Only call this method if the current module is **not** imported by a node-like runtime. |
270 | /// |
271 | /// Arguments `env` and `exports` must **not** be null. |
272 | pub unsafe extern "C" fn napi_register_module_v1( |
273 | env: sys::napi_env, |
274 | exports: sys::napi_value, |
275 | ) -> sys::napi_value { |
276 | if IS_FIRST_MODULE.load(Ordering::SeqCst) { |
277 | IS_FIRST_MODULE.store(false, Ordering::SeqCst); |
278 | } else { |
279 | wait_first_thread_registered(); |
280 | } |
281 | let mut exports_objects: HashSet<String> = HashSet::default(); |
282 | |
283 | { |
284 | let mut register_callback = MODULE_REGISTER_CALLBACK |
285 | .write() |
286 | .expect("Write MODULE_REGISTER_CALLBACK in napi_register_module_v1 failed" ); |
287 | register_callback |
288 | .iter_mut() |
289 | .fold( |
290 | HashMap::<Option<&'static str>, Vec<(&'static str, ExportRegisterCallback)>>::new(), |
291 | |mut acc, (js_mod, item)| { |
292 | if let Some(k) = acc.get_mut(js_mod) { |
293 | k.push(*item); |
294 | } else { |
295 | acc.insert(*js_mod, vec![*item]); |
296 | } |
297 | acc |
298 | }, |
299 | ) |
300 | .iter() |
301 | .for_each(|(js_mod, items)| { |
302 | let mut exports_js_mod = ptr::null_mut(); |
303 | if let Some(js_mod_str) = js_mod { |
304 | let mod_name_c_str = |
305 | unsafe { CStr::from_bytes_with_nul_unchecked(js_mod_str.as_bytes()) }; |
306 | if exports_objects.contains(*js_mod_str) { |
307 | check_status_or_throw!( |
308 | env, |
309 | unsafe { |
310 | sys::napi_get_named_property( |
311 | env, |
312 | exports, |
313 | mod_name_c_str.as_ptr(), |
314 | &mut exports_js_mod, |
315 | ) |
316 | }, |
317 | "Get mod {} from exports failed" , |
318 | js_mod_str, |
319 | ); |
320 | } else { |
321 | check_status_or_throw!( |
322 | env, |
323 | unsafe { sys::napi_create_object(env, &mut exports_js_mod) }, |
324 | "Create export JavaScript Object [ {}] failed" , |
325 | js_mod_str |
326 | ); |
327 | check_status_or_throw!( |
328 | env, |
329 | unsafe { |
330 | sys::napi_set_named_property(env, exports, mod_name_c_str.as_ptr(), exports_js_mod) |
331 | }, |
332 | "Set exports Object [ {}] into exports object failed" , |
333 | js_mod_str |
334 | ); |
335 | exports_objects.insert(js_mod_str.to_string()); |
336 | } |
337 | } |
338 | for (name, callback) in items { |
339 | unsafe { |
340 | let js_name = CStr::from_bytes_with_nul_unchecked(name.as_bytes()); |
341 | if let Err(e) = callback(env).and_then(|v| { |
342 | let exported_object = if exports_js_mod.is_null() { |
343 | exports |
344 | } else { |
345 | exports_js_mod |
346 | }; |
347 | check_status!( |
348 | sys::napi_set_named_property(env, exported_object, js_name.as_ptr(), v), |
349 | "Failed to register export ` {}`" , |
350 | name, |
351 | ) |
352 | }) { |
353 | JsError::from(e).throw_into(env) |
354 | } |
355 | } |
356 | } |
357 | }); |
358 | } |
359 | |
360 | let mut registered_classes = HashMap::new(); |
361 | |
362 | MODULE_CLASS_PROPERTIES.borrow_mut(|inner| { |
363 | inner.iter().for_each(|(rust_name, js_mods)| { |
364 | for (js_mod, (js_name, props)) in js_mods { |
365 | let mut exports_js_mod = ptr::null_mut(); |
366 | unsafe { |
367 | if let Some(js_mod_str) = js_mod { |
368 | let mod_name_c_str = CStr::from_bytes_with_nul_unchecked(js_mod_str.as_bytes()); |
369 | if exports_objects.contains(*js_mod_str) { |
370 | check_status_or_throw!( |
371 | env, |
372 | sys::napi_get_named_property( |
373 | env, |
374 | exports, |
375 | mod_name_c_str.as_ptr(), |
376 | &mut exports_js_mod, |
377 | ), |
378 | "Get mod {} from exports failed" , |
379 | js_mod_str, |
380 | ); |
381 | } else { |
382 | check_status_or_throw!( |
383 | env, |
384 | sys::napi_create_object(env, &mut exports_js_mod), |
385 | "Create export JavaScript Object [ {}] failed" , |
386 | js_mod_str |
387 | ); |
388 | check_status_or_throw!( |
389 | env, |
390 | sys::napi_set_named_property(env, exports, mod_name_c_str.as_ptr(), exports_js_mod), |
391 | "Set exports Object [ {}] into exports object failed" , |
392 | js_mod_str |
393 | ); |
394 | exports_objects.insert(js_mod_str.to_string()); |
395 | } |
396 | } |
397 | let (ctor, props): (Vec<_>, Vec<_>) = props.iter().partition(|prop| prop.is_ctor); |
398 | |
399 | let ctor = ctor |
400 | .first() |
401 | .map(|c| c.raw().method.unwrap()) |
402 | .unwrap_or(noop); |
403 | let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect(); |
404 | |
405 | let js_class_name = CStr::from_bytes_with_nul_unchecked(js_name.as_bytes()); |
406 | let mut class_ptr = ptr::null_mut(); |
407 | |
408 | check_status_or_throw!( |
409 | env, |
410 | sys::napi_define_class( |
411 | env, |
412 | js_class_name.as_ptr(), |
413 | js_name.len() - 1, |
414 | Some(ctor), |
415 | ptr::null_mut(), |
416 | raw_props.len(), |
417 | raw_props.as_ptr(), |
418 | &mut class_ptr, |
419 | ), |
420 | "Failed to register class ` {}` generate by struct ` {}`" , |
421 | &js_name, |
422 | &rust_name |
423 | ); |
424 | |
425 | let mut ctor_ref = ptr::null_mut(); |
426 | sys::napi_create_reference(env, class_ptr, 1, &mut ctor_ref); |
427 | |
428 | registered_classes.insert(js_name.to_string(), ctor_ref); |
429 | |
430 | check_status_or_throw!( |
431 | env, |
432 | sys::napi_set_named_property( |
433 | env, |
434 | if exports_js_mod.is_null() { |
435 | exports |
436 | } else { |
437 | exports_js_mod |
438 | }, |
439 | js_class_name.as_ptr(), |
440 | class_ptr |
441 | ), |
442 | "Failed to register class ` {}` generate by struct ` {}`" , |
443 | &js_name, |
444 | &rust_name |
445 | ); |
446 | } |
447 | } |
448 | }); |
449 | |
450 | REGISTERED_CLASSES.borrow_mut(|map| { |
451 | map.insert( |
452 | std::thread::current().id(), |
453 | PersistedPerInstanceHashMap::from_hashmap(registered_classes), |
454 | ) |
455 | }); |
456 | }); |
457 | |
458 | #[cfg (feature = "compat-mode" )] |
459 | { |
460 | let module_exports = MODULE_EXPORTS.read().expect("Read MODULE_EXPORTS failed" ); |
461 | module_exports.iter().for_each(|callback| unsafe { |
462 | if let Err(e) = callback(env, exports) { |
463 | JsError::from(e).throw_into(env); |
464 | } |
465 | }) |
466 | } |
467 | |
468 | #[cfg (all( |
469 | not(any(target_os = "macos" , target_family = "wasm" )), |
470 | feature = "napi4" , |
471 | feature = "tokio_rt" |
472 | ))] |
473 | { |
474 | crate::tokio_runtime::ensure_runtime(); |
475 | |
476 | static init_counter: AtomicUsize = AtomicUsize::new(0); |
477 | let cleanup_hook_payload = |
478 | init_counter.fetch_add(1, Ordering::Relaxed) as *mut std::ffi::c_void; |
479 | |
480 | unsafe { |
481 | sys::napi_add_env_cleanup_hook( |
482 | env, |
483 | Some(crate::tokio_runtime::drop_runtime), |
484 | cleanup_hook_payload, |
485 | ) |
486 | }; |
487 | } |
488 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" )))] |
489 | create_custom_gc(env); |
490 | FIRST_MODULE_REGISTERED.store(true, Ordering::SeqCst); |
491 | exports |
492 | } |
493 | |
494 | #[cfg (not(feature = "noop" ))] |
495 | pub(crate) unsafe extern "C" fn noop( |
496 | env: sys::napi_env, |
497 | _info: sys::napi_callback_info, |
498 | ) -> sys::napi_value { |
499 | if !crate::bindgen_runtime::___CALL_FROM_FACTORY.with(|s: &AtomicBool| s.load(order:Ordering::Relaxed)) { |
500 | unsafe { |
501 | sys::napi_throw_error( |
502 | env, |
503 | code:ptr::null_mut(), |
504 | msg:CStr::from_bytes_with_nul_unchecked(bytes:b"Class contains no `constructor`, can not new it! \0" ) |
505 | .as_ptr(), |
506 | ); |
507 | } |
508 | } |
509 | ptr::null_mut() |
510 | } |
511 | |
512 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
513 | fn create_custom_gc(env: sys::napi_env) { |
514 | use std::os::raw::c_char; |
515 | |
516 | if !FIRST_MODULE_REGISTERED.load(Ordering::SeqCst) { |
517 | let mut custom_gc_fn = ptr::null_mut(); |
518 | check_status_or_throw!( |
519 | env, |
520 | unsafe { |
521 | sys::napi_create_function( |
522 | env, |
523 | "custom_gc" .as_ptr().cast(), |
524 | 9, |
525 | Some(empty), |
526 | ptr::null_mut(), |
527 | &mut custom_gc_fn, |
528 | ) |
529 | }, |
530 | "Create Custom GC Function in napi_register_module_v1 failed" |
531 | ); |
532 | let mut async_resource_name = ptr::null_mut(); |
533 | check_status_or_throw!( |
534 | env, |
535 | unsafe { |
536 | sys::napi_create_string_utf8( |
537 | env, |
538 | "CustomGC" .as_ptr() as *const c_char, |
539 | 8, |
540 | &mut async_resource_name, |
541 | ) |
542 | }, |
543 | "Create async resource string in napi_register_module_v1" |
544 | ); |
545 | let mut custom_gc_tsfn = ptr::null_mut(); |
546 | check_status_or_throw!( |
547 | env, |
548 | unsafe { |
549 | sys::napi_create_threadsafe_function( |
550 | env, |
551 | custom_gc_fn, |
552 | ptr::null_mut(), |
553 | async_resource_name, |
554 | 0, |
555 | 1, |
556 | ptr::null_mut(), |
557 | Some(custom_gc_finalize), |
558 | ptr::null_mut(), |
559 | Some(custom_gc), |
560 | &mut custom_gc_tsfn, |
561 | ) |
562 | }, |
563 | "Create Custom GC ThreadsafeFunction in napi_register_module_v1 failed" |
564 | ); |
565 | check_status_or_throw!( |
566 | env, |
567 | unsafe { sys::napi_unref_threadsafe_function(env, custom_gc_tsfn) }, |
568 | "Unref Custom GC ThreadsafeFunction in napi_register_module_v1 failed" |
569 | ); |
570 | CUSTOM_GC_TSFN.store(custom_gc_tsfn, Ordering::Relaxed); |
571 | } |
572 | |
573 | let current_thread_id = std::thread::current().id(); |
574 | THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.insert(current_thread_id, true)); |
575 | check_status_or_throw!( |
576 | env, |
577 | unsafe { |
578 | sys::napi_add_env_cleanup_hook( |
579 | env, |
580 | Some(remove_thread_id), |
581 | Box::into_raw(Box::new(current_thread_id)).cast(), |
582 | ) |
583 | }, |
584 | "Failed to add remove thread id cleanup hook" |
585 | ); |
586 | } |
587 | |
588 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
589 | unsafe extern "C" fn remove_thread_id(id: *mut std::ffi::c_void) { |
590 | let thread_id: Box = unsafe { Box::from_raw(id.cast::<ThreadId>()) }; |
591 | THREADS_CAN_ACCESS_ENV.borrow_mut(|m: &mut HashMap| m.insert(*thread_id, v:false)); |
592 | } |
593 | |
594 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
595 | #[allow (unused)] |
596 | unsafe extern "C" fn empty(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value { |
597 | ptr::null_mut() |
598 | } |
599 | |
600 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
601 | #[allow (unused_variables)] |
602 | unsafe extern "C" fn custom_gc_finalize( |
603 | env: sys::napi_env, |
604 | finalize_data: *mut std::ffi::c_void, |
605 | finalize_hint: *mut std::ffi::c_void, |
606 | ) { |
607 | CUSTOM_GC_TSFN_DESTROYED.store(val:true, order:Ordering::SeqCst); |
608 | } |
609 | |
610 | #[cfg (all(feature = "napi4" , not(target_family = "wasm" ), not(feature = "noop" )))] |
611 | // recycle the ArrayBuffer/Buffer Reference if the ArrayBuffer/Buffer is not dropped on the main thread |
612 | extern "C" fn custom_gc( |
613 | env: sys::napi_env, |
614 | _js_callback: sys::napi_value, |
615 | _context: *mut std::ffi::c_void, |
616 | data: *mut std::ffi::c_void, |
617 | ) { |
618 | // current thread was destroyed |
619 | if THREADS_CAN_ACCESS_ENV.borrow_mut(|m: &mut HashMap| m.get(&std::thread::current().id()) == Some(&false)) { |
620 | return; |
621 | } |
622 | let mut ref_count: u32 = 0; |
623 | check_status_or_throw!( |
624 | env, |
625 | unsafe { sys::napi_reference_unref(env, data.cast(), &mut ref_count) }, |
626 | "Failed to unref Buffer reference in Custom GC" |
627 | ); |
628 | debug_assert!( |
629 | ref_count == 0, |
630 | "Buffer reference count in Custom GC is not 0" |
631 | ); |
632 | check_status_or_throw!( |
633 | env, |
634 | unsafe { sys::napi_delete_reference(env, data.cast()) }, |
635 | "Failed to delete Buffer reference in Custom GC" |
636 | ); |
637 | } |
638 | |