33#include < cstdint>
44#include < cstring>
55#include < mutex>
6+ #include < thread>
67#include < unordered_map>
78#include " Interop.h"
89#include " ObjCBridge.h"
3738constexpr int kBlockHasCopyDispose = (1 << 25 );
3839constexpr int kBlockRefCountOne = (1 << 1 );
3940constexpr int kBlockHasSignature = (1 << 30 );
40- std::unordered_map<void *, napi_ref> g_blockToJsFunction;
41+
42+ struct BlockJsFunctionEntry {
43+ napi_ref ref = nullptr ;
44+ napi_env env = nullptr ;
45+ std::thread::id jsThreadId;
46+ CFRunLoopRef jsRunLoop = nullptr ;
47+ };
48+
49+ std::unordered_map<void *, BlockJsFunctionEntry> g_blockToJsFunction;
4150std::mutex g_blockToJsFunctionMutex;
4251
52+ inline bool removeCachedBlockJsFunctionEntry (void * blockPtr, BlockJsFunctionEntry* entry) {
53+ std::lock_guard<std::mutex> lock (g_blockToJsFunctionMutex);
54+ auto it = g_blockToJsFunction.find (blockPtr);
55+ if (it == g_blockToJsFunction.end ()) {
56+ return false ;
57+ }
58+ if (entry != nullptr ) {
59+ *entry = it->second ;
60+ }
61+ g_blockToJsFunction.erase (it);
62+ return true ;
63+ }
64+
65+ inline void deleteBlockReferenceOnOwningLoop (const BlockJsFunctionEntry& entry) {
66+ if (entry.ref == nullptr || entry.env == nullptr ) {
67+ return ;
68+ }
69+
70+ if (entry.jsThreadId == std::this_thread::get_id ()) {
71+ napi_delete_reference (entry.env , entry.ref );
72+ return ;
73+ }
74+
75+ CFRunLoopRef runLoop = entry.jsRunLoop ;
76+ if (runLoop == nullptr ) {
77+ runLoop = CFRunLoopGetMain ();
78+ }
79+
80+ if (runLoop == nullptr ) {
81+ return ;
82+ }
83+
84+ CFRetain (runLoop);
85+ napi_env env = entry.env ;
86+ napi_ref ref = entry.ref ;
87+ CFRunLoopPerformBlock (runLoop, kCFRunLoopCommonModes , ^{
88+ napi_delete_reference (env, ref);
89+ CFRelease (runLoop);
90+ });
91+ CFRunLoopWakeUp (runLoop);
92+ }
93+
4394void block_copy (void * dest, void * src) {
4495 auto dst = static_cast <Block_literal_1*>(dest);
4596 auto source = static_cast <Block_literal_1*>(src);
@@ -55,15 +106,9 @@ void block_release(void* src) {
55106 return ;
56107 }
57108
58- if (block->closure != nullptr && block->closure ->env != nullptr ) {
59- std::lock_guard<std::mutex> lock (g_blockToJsFunctionMutex);
60- auto it = g_blockToJsFunction.find (block);
61- if (it != g_blockToJsFunction.end ()) {
62- if (std::this_thread::get_id () == block->closure ->jsThreadId ) {
63- napi_delete_reference (block->closure ->env , it->second );
64- }
65- g_blockToJsFunction.erase (it);
66- }
109+ BlockJsFunctionEntry entry;
110+ if (removeCachedBlockJsFunctionEntry (block, &entry)) {
111+ deleteBlockReferenceOnOwningLoop (entry);
67112 }
68113
69114 if (block->closure != nullptr ) {
@@ -81,20 +126,34 @@ void block_release(void* src) {
81126};
82127
83128inline napi_value getCachedBlockJsFunction (napi_env env, void * blockPtr) {
84- std::lock_guard<std::mutex> lock (g_blockToJsFunctionMutex);
85- auto it = g_blockToJsFunction.find (blockPtr);
86- if (it == g_blockToJsFunction.end ()) {
87- return nullptr ;
129+ BlockJsFunctionEntry removedEntry;
130+ bool shouldDelete = false ;
131+ napi_value value = nullptr ;
132+
133+ {
134+ std::lock_guard<std::mutex> lock (g_blockToJsFunctionMutex);
135+ auto it = g_blockToJsFunction.find (blockPtr);
136+ if (it == g_blockToJsFunction.end ()) {
137+ return nullptr ;
138+ }
139+
140+ value = nativescript::get_ref_value (env, it->second .ref );
141+ if (value == nullptr ) {
142+ removedEntry = it->second ;
143+ g_blockToJsFunction.erase (it);
144+ shouldDelete = true ;
145+ }
88146 }
89- napi_value value = nativescript::get_ref_value (env, it->second );
90- if (value == nullptr ) {
91- napi_delete_reference (env, it->second );
92- g_blockToJsFunction.erase (it);
147+
148+ if (shouldDelete) {
149+ deleteBlockReferenceOnOwningLoop (removedEntry);
93150 }
151+
94152 return value;
95153}
96154
97- inline void cacheBlockJsFunction (napi_env env, void * blockPtr, napi_value jsFunction) {
155+ inline void cacheBlockJsFunction (napi_env env, void * blockPtr, napi_value jsFunction,
156+ nativescript::Closure* closure) {
98157 if (blockPtr == nullptr || jsFunction == nullptr ) {
99158 return ;
100159 }
@@ -103,24 +162,27 @@ inline void cacheBlockJsFunction(napi_env env, void* blockPtr, napi_value jsFunc
103162 return ;
104163 }
105164 // Keep this weak so callback identity can round-trip without preventing GC.
106- g_blockToJsFunction[blockPtr] = nativescript::make_ref (env, jsFunction, 0 );
165+ BlockJsFunctionEntry entry;
166+ entry.ref = nativescript::make_ref (env, jsFunction, 0 );
167+ entry.env = env;
168+ entry.jsThreadId = closure != nullptr ? closure->jsThreadId : std::this_thread::get_id ();
169+ entry.jsRunLoop = closure != nullptr ? closure->jsRunLoop : CFRunLoopGetCurrent ();
170+ g_blockToJsFunction[blockPtr] = entry;
107171}
108172
109173} // namespace
110174
111175void block_finalize (napi_env env, void * data, void * hint) {
176+ (void )env;
177+ (void )hint;
112178 auto block = static_cast <Block_literal_1*>(data);
113179 if (block == nullptr ) {
114180 return ;
115181 }
116182
117- {
118- std::lock_guard<std::mutex> lock (g_blockToJsFunctionMutex);
119- auto it = g_blockToJsFunction.find (block);
120- if (it != g_blockToJsFunction.end ()) {
121- napi_delete_reference (env, it->second );
122- g_blockToJsFunction.erase (it);
123- }
183+ BlockJsFunctionEntry entry;
184+ if (removeCachedBlockJsFunctionEntry (block, &entry)) {
185+ deleteBlockReferenceOnOwningLoop (entry);
124186 }
125187
126188 if (block->closure != nullptr ) {
@@ -175,7 +237,7 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) {
175237 }
176238#endif // ENABLE_JS_RUNTIME
177239
178- cacheBlockJsFunction (env, block, callback);
240+ cacheBlockJsFunction (env, block, callback, closure );
179241
180242 return (id )block;
181243}
0 commit comments