[ReactNative] Fix racing conditions on reload

Summary:
@public

That was eventually being released before all the queues had been cleared.
Update it so the each modules' queue is immediately invalidated after sending
the `-invalidate` message to it, and introduce an intentional retain cycle so
the bridge is only released together with all modules, when all the messages
have been dispatched.

Test Plan: Launch the UIExplorer, and reload it, like, a lot.
This commit is contained in:
Tadeu Zagallo 2015-06-22 04:55:55 -07:00
parent 2cb0546d15
commit 9228873fb4
1 changed files with 36 additions and 17 deletions

View File

@ -1042,11 +1042,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
- (NSDictionary *)modules - (NSDictionary *)modules
{ {
if (!self.isValid) { RCTAssert(!self.isValid || _modulesByName != nil, @"Bridge modules have not yet been initialized. "
return nil;
}
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. "
"You may be trying to access a module too early in the startup procedure."); "You may be trying to access a module too early in the startup procedure.");
return _modulesByName; return _modulesByName;
@ -1074,19 +1070,21 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
_mainDisplayLink = nil; _mainDisplayLink = nil;
// Invalidate modules // Invalidate modules
dispatch_group_t group = dispatch_group_create();
for (id target in _modulesByID.allObjects) { for (id target in _modulesByID.allObjects) {
if ([target respondsToSelector:@selector(invalidate)]) { if ([target respondsToSelector:@selector(invalidate)]) {
[self dispatchBlock:^{ [self dispatchBlock:^{
[(id<RCTInvalidating>)target invalidate]; [(id<RCTInvalidating>)target invalidate];
} forModule:target]; } forModule:target dispatchGroup:group];
} }
_queuesByID[RCTModuleIDsByName[RCTBridgeModuleNameForClass([target class])]] = nil;
} }
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Release modules (breaks retain cycle if module has strong bridge reference)
_frameUpdateObservers = nil;
_modulesByID = nil;
_queuesByID = nil; _queuesByID = nil;
_modulesByID = nil;
_modulesByName = nil; _modulesByName = nil;
_frameUpdateObservers = nil;
});
}; };
if (!_javaScriptExecutor) { if (!_javaScriptExecutor) {
@ -1185,13 +1183,30 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
} }
#pragma mark - Payload Generation #pragma mark - Payload Generation
- (void)dispatchBlock:(dispatch_block_t)block
- (void)dispatchBlock:(dispatch_block_t)block forModule:(id<RCTBridgeModule>)module forModule:(id<RCTBridgeModule>)module
{ {
[self dispatchBlock:block forModuleID:RCTModuleIDsByName[RCTBridgeModuleNameForClass([module class])]]; [self dispatchBlock:block forModule:module dispatchGroup:NULL];
} }
- (void)dispatchBlock:(dispatch_block_t)block forModuleID:(NSNumber *)moduleID - (void)dispatchBlock:(dispatch_block_t)block
forModule:(id<RCTBridgeModule>)module
dispatchGroup:(dispatch_group_t)group
{
[self dispatchBlock:block
forModuleID:RCTModuleIDsByName[RCTBridgeModuleNameForClass([module class])]
dispatchGroup:group];
}
- (void)dispatchBlock:(dispatch_block_t)block
forModuleID:(NSNumber *)moduleID
{
[self dispatchBlock:block forModuleID:moduleID dispatchGroup:NULL];
}
- (void)dispatchBlock:(dispatch_block_t)block
forModuleID:(NSNumber *)moduleID
dispatchGroup:(dispatch_group_t)group
{ {
RCTAssertJSThread(); RCTAssertJSThread();
@ -1203,8 +1218,12 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
if (queue == RCTJSThread) { if (queue == RCTJSThread) {
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; [_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
} else if (queue) { } else if (queue) {
if (group != NULL) {
dispatch_group_async(group, queue, block);
} else {
dispatch_async(queue, block); dispatch_async(queue, block);
} }
}
} }
/** /**