diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h index e419f66a6de752..1277561b71c5b4 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h @@ -143,6 +143,7 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { size_t count); id instance_; + std::shared_ptr nativeInvoker_; protected: void setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName); diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm index ba0923da2505f8..20caa031d987e9 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm @@ -360,39 +360,10 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( [performanceLogger syncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; }; - // Backward-compatibility layer for calling module methods on specific queue. - dispatch_queue_t methodQueue = NULL; - if ([instance_ conformsToProtocol:@protocol(RCTBridgeModule)] && - [instance_ respondsToSelector:@selector(methodQueue)]) { - methodQueue = [instance_ performSelector:@selector(methodQueue)]; - } - - if (methodQueue == NULL || methodQueue == RCTJSThread) { - // This is the default mode of execution: on JS thread. - block(); - } else if (methodQueue == dispatch_get_main_queue()) { - if (returnType == VoidKind) { - // Void methods are treated as async for now, so there's no need to block here. - - [performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName - methodName:methodName - methodCallId:methodCallId]; - RCTExecuteOnMainQueue(block); - } else { - // This is not ideal, but provides the simplest mechanism for now. - // Eventually, methods should be responsible to queue things up to different queue if they need to. - // TODO: consider adding timer to warn if this method invocation takes too long. - RCTUnsafeExecuteOnMainQueueSync(block); - } + if (returnType == VoidKind) { + nativeInvoker_->invokeAsync([block]() -> void { block(); }); } else { - if (returnType == VoidKind) { - [performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName - methodName:methodName - methodCallId:methodCallId]; - dispatch_async(methodQueue, block); - } else { - dispatch_sync(methodQueue, block); - } + nativeInvoker_->invokeSync([block]() -> void { block(); }); } // VoidKind can't be null @@ -640,7 +611,7 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( std::shared_ptr jsInvoker, std::shared_ptr nativeInvoker, id perfLogger) - : TurboModule(name, jsInvoker), instance_(instance), performanceLogger_(perfLogger) + : TurboModule(name, jsInvoker), instance_(instance), nativeInvoker_(nativeInvoker), performanceLogger_(perfLogger) { } diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm index 791075f2378548..a2061d1ea86333 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm @@ -30,6 +30,41 @@ */ static char kAssociatedMethodQueueKey; +namespace { +class MethodQueueNativeCallInvoker : public facebook::react::CallInvoker { + private: + dispatch_queue_t methodQueue_; + + public: + MethodQueueNativeCallInvoker(dispatch_queue_t methodQueue) : methodQueue_(methodQueue) {} + void invokeAsync(std::function &&work) override + { + if (methodQueue_ == RCTJSThread) { + work(); + return; + } + + __block auto retainedWork = std::move(work); + dispatch_async(methodQueue_, ^{ + retainedWork(); + }); + } + + void invokeSync(std::function &&work) override + { + if (methodQueue_ == RCTJSThread) { + work(); + return; + } + + __block auto retainedWork = std::move(work); + dispatch_sync(methodQueue_, ^{ + retainedWork(); + }); + } +}; +} + // Fallback lookup since RCT class prefix is sometimes stripped in the existing NativeModule system. // This will be removed in the future. static Class getFallbackClassFromName(const char *name) @@ -164,12 +199,28 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name Class moduleClass = [module class]; + dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey); + + /** + * Step 2c: Create and native CallInvoker from the TurboModule's method queue. + */ + std::shared_ptr nativeInvoker = + std::make_shared(methodQueue); + + /** + * Have RCTCxxBridge decorate native CallInvoker, so that it's aware of TurboModule async method calls. + * This helps the bridge fire onBatchComplete as readily as it should. + */ + if ([_bridge respondsToSelector:@selector(decorateNativeCallInvoker:)]) { + nativeInvoker = [_bridge decorateNativeCallInvoker:nativeInvoker]; + } + // If RCTTurboModule supports creating its own C++ TurboModule object, // allow it to do so. if ([module respondsToSelector:@selector(getTurboModuleWithJsInvoker:nativeInvoker:perfLogger:)]) { [_performanceLogger getTurboModuleFromRCTTurboModuleStart:moduleName]; auto turboModule = [module getTurboModuleWithJsInvoker:_jsInvoker - nativeInvoker:nullptr + nativeInvoker:nativeInvoker perfLogger:_performanceLogger]; [_performanceLogger getTurboModuleFromRCTTurboModuleEnd:moduleName]; assert(turboModule != nullptr); @@ -178,7 +229,7 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name } /** - * Step 2c: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that + * Step 2d: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that * wraps CxxModule. */ if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) { @@ -192,13 +243,13 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name } /** - * Step 2d: Return an exact sub-class of ObjC TurboModule + * Step 2e: Return an exact sub-class of ObjC TurboModule */ [_performanceLogger getTurboModuleFromTMMDelegateStart:moduleName]; auto turboModule = [_delegate getTurboModule:moduleName instance:module jsInvoker:_jsInvoker - nativeInvoker:nullptr + nativeInvoker:nativeInvoker perfLogger:_performanceLogger]; [_performanceLogger getTurboModuleFromTMMDelegateEnd:moduleName]; if (turboModule != nullptr) {