Make TurboModules dispatch method calls via native CallInvoker

Summary:
This diff:
1. Has ObjC NativeModules use the native `CallInvoker` to invoke JS -> native sync/async calls.
2. Integrates the native `CallInvoker` for each ObjC NativeModule with the bridge. This way, the bridge is informed of all JS -> native TurboModule method calls, and dispatches `onBatchComplete` appropriately.

Changelog:
[iOS][Fixed] Integrate ObjC TurboModules async method calls with the bridge

Reviewed By: fkgozali

Differential Revision: D20831545

fbshipit-source-id: da1cbb4ecef4cae85841ca7ef625ab8e380760cd
This commit is contained in:
Ramanpreet Nara 2020-04-03 17:51:37 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 3246f68952
Коммит 21733641a8
3 изменённых файлов: 60 добавлений и 37 удалений

Просмотреть файл

@ -143,6 +143,7 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
size_t count);
id<RCTTurboModule> instance_;
std::shared_ptr<CallInvoker> nativeInvoker_;
protected:
void setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName);

Просмотреть файл

@ -360,39 +360,10 @@ jsi::Value ObjCTurboModule::performMethodInvocation(
[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 @@ ObjCTurboModule::ObjCTurboModule(
std::shared_ptr<CallInvoker> jsInvoker,
std::shared_ptr<CallInvoker> nativeInvoker,
id<RCTTurboModulePerformanceLogger> perfLogger)
: TurboModule(name, jsInvoker), instance_(instance), performanceLogger_(perfLogger)
: TurboModule(name, jsInvoker), instance_(instance), nativeInvoker_(nativeInvoker), performanceLogger_(perfLogger)
{
}

Просмотреть файл

@ -30,6 +30,41 @@ using namespace facebook;
*/
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<void()> &&work) override
{
if (methodQueue_ == RCTJSThread) {
work();
return;
}
__block auto retainedWork = std::move(work);
dispatch_async(methodQueue_, ^{
retainedWork();
});
}
void invokeSync(std::function<void()> &&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 @@ static Class getFallbackClassFromName(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<facebook::react::CallInvoker> nativeInvoker =
std::make_shared<MethodQueueNativeCallInvoker>(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 @@ static Class getFallbackClassFromName(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 @@ static Class getFallbackClassFromName(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) {