objc-js 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  "format": "prettier --write \"**/*.{ts,js,json,md}\"",
40
40
  "preinstall-disabled": "npm run build-scripts && npm run make-clangd-config"
41
41
  },
42
- "version": "0.0.9",
42
+ "version": "0.0.11",
43
43
  "description": "Objective-C bridge for Node.js",
44
44
  "main": "dist/index.js",
45
45
  "dependencies": {
@@ -551,7 +551,15 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
551
551
  js_thread = it->second.js_thread;
552
552
 
553
553
  // Get the ThreadSafeFunction - this is thread-safe by design
554
+ // IMPORTANT: We must Acquire() to increment the ref count, because copying
555
+ // a ThreadSafeFunction does NOT increment it. If DeallocImplementation runs
556
+ // and calls Release() on the original, our copy would become invalid.
554
557
  tsfn = callbackIt->second;
558
+ napi_status acq_status = tsfn.Acquire();
559
+ if (acq_status != napi_ok) {
560
+ NSLog(@"Warning: Failed to acquire ThreadSafeFunction for selector %s", selectorName.c_str());
561
+ return;
562
+ }
555
563
 
556
564
  // Get the type encoding for return value handling
557
565
  auto encIt = it->second.typeEncodings.find(selectorName);
@@ -571,9 +579,14 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
571
579
 
572
580
  if (is_js_thread) {
573
581
  // We're on the JS thread, so it's safe to access N-API values directly
582
+ // Release the TSFN we acquired - we don't need it on this path
583
+ tsfn.Release();
584
+
574
585
  // Do a second lookup to get the JS callback
586
+ // IMPORTANT: We must call .Value() while holding the lock to prevent
587
+ // DeallocImplementation from erasing the entry while we're using it
575
588
  napi_env stored_env;
576
- Napi::FunctionReference* jsCallbackPtr = nullptr;
589
+ Napi::Function jsFn;
577
590
  {
578
591
  std::lock_guard<std::mutex> lock(g_implementations_mutex);
579
592
  auto it = g_implementations.find(ptr);
@@ -591,12 +604,13 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
591
604
  }
592
605
 
593
606
  stored_env = it->second.env;
594
- jsCallbackPtr = &jsCallbackIt->second;
607
+ // Get the function value WHILE HOLDING THE LOCK
608
+ // This prevents a race with DeallocImplementation clearing the jsCallbacks map
609
+ jsFn = jsCallbackIt->second.Value();
595
610
  }
596
611
 
597
- // Now we can safely access N-API values since we're on the JS thread
612
+ // Now jsFn is a local Napi::Function (napi_value), safe to use after lock release
598
613
  Napi::Env env(stored_env);
599
- Napi::Function jsFn = jsCallbackPtr->Value(); // Safe: on JS thread with valid HandleScope
600
614
  CallJSCallback(env, jsFn, data);
601
615
  // Data is deleted in CallJSCallback
602
616
  } else {
@@ -604,6 +618,10 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
604
618
  // Use ThreadSafeFunction to marshal to JS thread
605
619
  napi_status status = tsfn.BlockingCall(data, CallJSCallback);
606
620
 
621
+ // Release our acquired reference to the TSFN
622
+ // This balances the Acquire() call above
623
+ tsfn.Release();
624
+
607
625
  if (status != napi_ok) {
608
626
  NSLog(@"Error: Failed to call ThreadSafeFunction for selector %s (status: %d)",
609
627
  selectorName.c_str(), status);