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 {
NSMutableDictionary *_eventHandlers;
__weak NSThread *_currentJSThread;
__weak NSRunLoop *_currentJSRunLoop;
#if DEBUG
GCDWebServer *_webServer;
@ -261,17 +259,6 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) {
// shutdown rpc if in chrome debug mode
[self shutdownRPC];
#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 {
@ -296,15 +283,24 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) {
}
else {
__weak __typeof__(self) weakSelf = self;
__weak __typeof__(executor) weakExecutor = executor;
[executor executeBlockOnJavaScriptQueue:^{
__typeof__(self) self = weakSelf;
if (!self) {
__typeof__(executor) executor = weakExecutor;
if (!self || !executor) {
return;
}
self->_currentJSThread = [NSThread currentThread];
self->_currentJSRunLoop = [NSRunLoop currentRunLoop];
// Make sure the previous JS thread is completely finished before continuing.
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);
RJSInitializeInContext(ctx);

View File

@ -78,23 +78,26 @@ extern NSMutableArray *RCTGetModuleClasses(void);
return nil;
}
RCTBridge *bridge = [RCTBridge currentBridge];
if (!bridge.valid) {
[self waitForNotification:RCTJavaScriptDidLoadNotification];
bridge = [RCTBridge currentBridge];
@autoreleasepool {
RCTBridge *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 {
@ -102,37 +105,39 @@ extern NSMutableArray *RCTGetModuleClasses(void);
}
+ (XCTestSuite *)defaultTestSuite {
XCTestSuite *suite = [super defaultTestSuite];
id<RCTJavaScriptExecutor> executor = [self currentExecutor];
@autoreleasepool {
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;
}
// 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 {