diff --git a/React/Base/RCTModuleData.h b/React/Base/RCTModuleData.h index aa1c4896feae3f..40b8d18f7af39f 100644 --- a/React/Base/RCTModuleData.h +++ b/React/Base/RCTModuleData.h @@ -58,14 +58,27 @@ typedef id(^RCTBridgeModuleProvider)(void); /** * Returns YES if module instance must be created on the main thread. + * May be overriden by "allowOffMainQueueRegistration". */ @property (nonatomic, assign, readonly) BOOL requiresMainQueueSetup; /** * Returns YES if module has constants to export. + * May be overriden by "allowOffMainQueueRegistration". */ @property (nonatomic, assign, readonly) BOOL hasConstantsToExport; +/** + * If set to YES, it will force both setup and constants export process + * to explicitly happen off the main queue. + * Overrides "requiresMainQueueSetup" & "hasConstantsToExport" + * Defaults to NO. + * + * @experimental + */ + +@property (nonatomic, assign) BOOL allowOffMainQueueRegistration; + /** * Returns the current module instance. Note that this will init the instance * if it has not already been created. To check if the module instance exists diff --git a/React/Base/RCTModuleData.mm b/React/Base/RCTModuleData.mm index e4254d0c26ad45..edad584674b7d2 100644 --- a/React/Base/RCTModuleData.mm +++ b/React/Base/RCTModuleData.mm @@ -101,7 +101,7 @@ - (void)setUpInstanceAndBridge if (!_setupComplete && _bridge.valid) { if (!_instance) { - if (RCT_DEBUG && _requiresMainQueueSetup) { + if (RCT_DEBUG && _requiresMainQueueSetup && !_allowOffMainQueueRegistration) { RCTAssertMainQueue(); } RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] Create module", nil); @@ -222,7 +222,7 @@ - (BOOL)hasInstance { if (!_setupComplete) { RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData instanceForClass:%@]", _moduleClass]), nil); - if (_requiresMainQueueSetup) { + if (_requiresMainQueueSetup && !_allowOffMainQueueRegistration) { // The chances of deadlock here are low, because module init very rarely // calls out to other threads, however we can't control when a module might // get accessed by client code during bridge setup, and a very low risk of @@ -295,13 +295,17 @@ - (void)gatherConstants if (_hasConstantsToExport && !_constantsToExport) { RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass]), nil); (void)[self instance]; - if (!RCTIsMainQueue()) { - RCTLogWarn(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass); - } + if (_allowOffMainQueueRegistration) { + _constantsToExport = [_instance constantsToExport] ?: @{}; + } else { + if (!RCTIsMainQueue()) { + RCTLogWarn(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass); + } - RCTUnsafeExecuteOnMainQueueSync(^{ - self->_constantsToExport = [self->_instance constantsToExport] ?: @{}; - }); + RCTUnsafeExecuteOnMainQueueSync(^{ + self->_constantsToExport = [self->_instance constantsToExport] ?: @{}; + }); + } RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); } }