Skip to content

Commit 46bf430

Browse files
committed
fix(react-native): preserve ObjC block callback threads
1 parent ae3cb13 commit 46bf430

4 files changed

Lines changed: 41 additions & 2 deletions

File tree

NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,8 @@ class NativeApiJsiCallback final
426426
gActiveSynchronousNativeInvocationDepth.load(
427427
std::memory_order_acquire) > 0;
428428
bool nativeCallerThreadCallback =
429-
!currentThreadIsJs && activeSynchronousNativeInvocation &&
430-
(!block_ || !returnsVoid);
429+
!currentThreadIsJs &&
430+
(block_ || (activeSynchronousNativeInvocation && !returnsVoid));
431431
bool direct = currentThreadIsJs ||
432432
gExecutingDispatchedUINativeCall ||
433433
gSynchronousNativeInvocationDepth > 0 ||

test/react-native/ffi-compat/App.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,32 @@ function buildReactNativeIntegrationTests(): TestCase[] {
762762
assert(callbackRan, 'background callback did not run');
763763
},
764764
},
765+
{
766+
name: 'invokes Objective-C block callbacks on the native caller thread',
767+
run() {
768+
let callbackRan = false;
769+
let callbackThreadHash: string | null = null;
770+
const nativeThreadHash = g('TNSObjCTypes')
771+
.alloc()
772+
.init()
773+
.methodWithSimpleBlockOnBackground((callerThreadHash: string) => {
774+
callbackRan = true;
775+
callbackThreadHash = String(g('NSThread').currentThread.hash);
776+
assertEqual(
777+
callbackThreadHash,
778+
String(callerThreadHash),
779+
'block callback JS native calls thread',
780+
);
781+
});
782+
783+
assert(callbackRan, 'background block callback did not run');
784+
assertEqual(
785+
callbackThreadHash,
786+
String(nativeThreadHash),
787+
'background block callback return thread',
788+
);
789+
},
790+
},
765791
{
766792
name: 'runs UIKit native calls through runOnUI main-thread dispatch',
767793
async run() {

test/runtime/fixtures/Marshalling/TNSObjCTypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ typedef int (^NumberReturner)(int, int, int);
2121
- (void)methodWithStructOutParameter:(TNSOStruct*)value;
2222

2323
- (void)methodWithSimpleBlock:(void (^)(void))block;
24+
- (NSString*)methodWithSimpleBlockOnBackground:(void (^)(NSString* callerThreadHash))block;
2425
- (void)methodWithComplexBlock:(id (^)(int, id, SEL, NSObject*, TNSOStruct))block;
2526

2627
- (void)methodRetainingBlock:(void (^)(void))block;

test/runtime/fixtures/Marshalling/TNSObjCTypes.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ - (void)methodWithSimpleBlock:(void (^)(void))block {
4848
block();
4949
}
5050

51+
- (NSString*)methodWithSimpleBlockOnBackground:(void (^)(NSString* callerThreadHash))block {
52+
__block NSString* nativeThreadHash = nil;
53+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
54+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
55+
nativeThreadHash = [NSString stringWithFormat:@"%lu", (unsigned long)NSThread.currentThread.hash];
56+
block(nativeThreadHash);
57+
dispatch_semaphore_signal(semaphore);
58+
});
59+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
60+
return nativeThreadHash;
61+
}
62+
5163
- (void)methodRetainingBlock:(void (^)(void))block {
5264
_retainedBlock = block;
5365
}

0 commit comments

Comments
 (0)