Make React reloads and testing more reliable

Changed where we wait on the previous JS thread, which ultimately makes it more reliable and no longer leak memory on reloads.

Resolves #397
This commit is contained in:
Scott Kyle 2016-04-25 16:54:39 -07:00
parent 933e6070c5
commit db1283255f
2 changed files with 61 additions and 60 deletions

View File

@ -79,8 +79,6 @@ extern "C" JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executo
@implementation RealmReact { @implementation RealmReact {
NSMutableDictionary *_eventHandlers; NSMutableDictionary *_eventHandlers;
__weak NSThread *_currentJSThread;
__weak NSRunLoop *_currentJSRunLoop;
#if DEBUG #if DEBUG
GCDWebServer *_webServer; GCDWebServer *_webServer;
@ -261,17 +259,6 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) {
// shutdown rpc if in chrome debug mode // shutdown rpc if in chrome debug mode
[self shutdownRPC]; [self shutdownRPC];
#endif #endif
// block until JS thread exits
NSRunLoop *runLoop = _currentJSRunLoop;
if (runLoop) {
CFRunLoopStop([runLoop getCFRunLoop]);
while (_currentJSThread && !_currentJSThread.finished) {
[NSThread sleepForTimeInterval:0.01];
}
}
realm::_impl::RealmCoordinator::clear_all_caches();
} }
- (void)dealloc { - (void)dealloc {
@ -296,15 +283,24 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) {
} }
else { else {
__weak __typeof__(self) weakSelf = self; __weak __typeof__(self) weakSelf = self;
__weak __typeof__(executor) weakExecutor = executor;
[executor executeBlockOnJavaScriptQueue:^{ [executor executeBlockOnJavaScriptQueue:^{
__typeof__(self) self = weakSelf; __typeof__(self) self = weakSelf;
if (!self) { __typeof__(executor) executor = weakExecutor;
if (!self || !executor) {
return; return;
} }
self->_currentJSThread = [NSThread currentThread]; // Make sure the previous JS thread is completely finished before continuing.
self->_currentJSRunLoop = [NSRunLoop currentRunLoop]; static __weak NSThread *s_currentJSThread;
while (s_currentJSThread && !s_currentJSThread.finished) {
[NSThread sleepForTimeInterval:0.1];
}
s_currentJSThread = [NSThread currentThread];
// Close all cached Realms from the previous JS thread.
realm::_impl::RealmCoordinator::clear_all_caches();
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, true); JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, true);
RJSInitializeInContext(ctx); RJSInitializeInContext(ctx);

View File

@ -78,23 +78,26 @@ extern NSMutableArray *RCTGetModuleClasses(void);
return nil; return nil;
} }
RCTBridge *bridge = [RCTBridge currentBridge]; @autoreleasepool {
if (!bridge.valid) { RCTBridge *bridge = [RCTBridge currentBridge];
[self waitForNotification:RCTJavaScriptDidLoadNotification];
bridge = [RCTBridge currentBridge]; if (!bridge.valid) {
[self waitForNotification:RCTJavaScriptDidLoadNotification];
bridge = [RCTBridge currentBridge];
}
if (bridge.executorClass != executorClass) {
bridge.executorClass = executorClass;
RCTBridge *parentBridge = [bridge valueForKey:@"parentBridge"];
[parentBridge invalidate];
[parentBridge setUp];
return [self currentBridge];
}
return bridge;
} }
if (bridge.executorClass != executorClass) {
bridge.executorClass = executorClass;
RCTBridge *parentBridge = [bridge valueForKey:@"parentBridge"];
[parentBridge invalidate];
[parentBridge setUp];
return [self currentBridge];
}
return bridge;
} }
+ (id<RCTJavaScriptExecutor>)currentExecutor { + (id<RCTJavaScriptExecutor>)currentExecutor {
@ -102,37 +105,39 @@ extern NSMutableArray *RCTGetModuleClasses(void);
} }
+ (XCTestSuite *)defaultTestSuite { + (XCTestSuite *)defaultTestSuite {
XCTestSuite *suite = [super defaultTestSuite]; @autoreleasepool {
id<RCTJavaScriptExecutor> executor = [self currentExecutor]; XCTestSuite *suite = [super defaultTestSuite];
id<RCTJavaScriptExecutor> executor = [self currentExecutor];
// The executor may be nil if the executorClass was not found (i.e. release build).
if (!executor) {
return suite;
}
// FIXME: Remove this nonsense once the crashes go away when a test fails!
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, false);
if (ctx) {
JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(ctx, false);
}
NSDictionary *testCaseNames = [self waitForEvent:@"realm-test-names"];
NSAssert(testCaseNames.count, @"No test names were provided by the JS");
NSString *nameSuffix = [self classNameSuffix];
if (nameSuffix.length) {
NSMutableDictionary *renamedTestCaseNames = [[NSMutableDictionary alloc] init];
for (NSString *name in testCaseNames) {
renamedTestCaseNames[[name stringByAppendingString:nameSuffix]] = testCaseNames[name];
}
testCaseNames = renamedTestCaseNames;
}
for (XCTestSuite *testSuite in [self testSuitesFromDictionary:testCaseNames]) {
[suite addTest:testSuite];
}
// The executor may be nil if the executorClass was not found (i.e. release build).
if (!executor) {
return suite; return suite;
} }
// FIXME: Remove this nonsense once the crashes go away when a test fails!
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, false);
if (ctx) {
JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(ctx, false);
}
NSDictionary *testCaseNames = [self waitForEvent:@"realm-test-names"];
NSAssert(testCaseNames.count, @"No test names were provided by the JS");
NSString *nameSuffix = [self classNameSuffix];
if (nameSuffix.length) {
NSMutableDictionary *renamedTestCaseNames = [[NSMutableDictionary alloc] init];
for (NSString *name in testCaseNames) {
renamedTestCaseNames[[name stringByAppendingString:nameSuffix]] = testCaseNames[name];
}
testCaseNames = renamedTestCaseNames;
}
for (XCTestSuite *testSuite in [self testSuitesFromDictionary:testCaseNames]) {
[suite addTest:testSuite];
}
return suite;
} }
+ (NSNotification *)waitForNotification:(NSString *)notificationName { + (NSNotification *)waitForNotification:(NSString *)notificationName {