Pull CADisplayLink into a separate file

Reviewed By: javache

Differential Revision: D3107921

fb-gh-sync-id: 2982043280f925d390e4bebf3884d4762ac62cc6
fbshipit-source-id: 2982043280f925d390e4bebf3884d4762ac62cc6
This commit is contained in:
Marc Horowitz 2016-03-29 19:51:51 -07:00 committed by Facebook Github Bot 6
parent 68918e8773
commit 2115664456
3 changed files with 167 additions and 62 deletions

View File

@ -14,8 +14,8 @@
#import "RCTBridge+Private.h"
#import "RCTBridgeMethod.h"
#import "RCTConvert.h"
#import "RCTDisplayLink.h"
#import "RCTJSCExecutor.h"
#import "RCTFrameUpdate.h"
#import "RCTJavaScriptLoader.h"
#import "RCTLog.h"
#import "RCTModuleData.h"
@ -56,8 +56,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
NSArray<RCTModuleData *> *_moduleDataByID;
NSArray<Class> *_moduleClassesByID;
CADisplayLink *_jsDisplayLink;
NSMutableSet<RCTModuleData *> *_frameUpdateObservers;
RCTDisplayLink *_displayLink;
}
@synthesize flowID = _flowID;
@ -81,8 +80,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
_valid = YES;
_loading = YES;
_pendingCalls = [NSMutableArray new];
_frameUpdateObservers = [NSMutableSet new];
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
_displayLink = [RCTDisplayLink new];
[RCTBridge setCurrentBridge:self];
@ -409,20 +407,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleData:(RCTModuleData *)moduleData
{
if (![_frameUpdateObservers containsObject:moduleData]) {
if ([moduleData.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
[_frameUpdateObservers addObject:moduleData];
// Don't access the module instance via moduleData, as this will cause deadlock
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)module;
__weak typeof(self) weakSelf = self;
__weak typeof(_javaScriptExecutor) weakJavaScriptExecutor = _javaScriptExecutor;
observer.pauseCallback = ^{
[weakJavaScriptExecutor executeBlockOnJavaScriptQueue:^{
[weakSelf updateJSDisplayLinkState];
}];
};
}
}
[_displayLink registerModuleForFrameUpdates:module withModuleData:moduleData];
}
- (NSString *)moduleConfig
@ -441,21 +426,6 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
}, NULL);
}
- (void)updateJSDisplayLinkState
{
RCTAssertJSThread();
BOOL pauseDisplayLink = YES;
for (RCTModuleData *moduleData in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
if (!observer.paused) {
pauseDisplayLink = NO;
break;
}
}
_jsDisplayLink.paused = pauseDisplayLink;
}
- (void)injectJSONConfiguration:(NSString *)configJSON
onComplete:(void (^)(NSError *))onComplete
{
@ -492,7 +462,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
// Register the display link to start sending js calls after everything is setup
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTJSCExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
[_displayLink addToRunLoop:targetRunLoop];
// Perform the state update and notification on the main thread, so we can't run into
// timing issues with RCTRootView
@ -643,8 +613,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
[_jsDisplayLink invalidate];
_jsDisplayLink = nil;
[_displayLink invalidate];
_displayLink = nil;
[_javaScriptExecutor invalidate];
_javaScriptExecutor = nil;
@ -656,7 +626,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
_moduleDataByName = nil;
_moduleDataByID = nil;
_moduleClassesByID = nil;
_frameUpdateObservers = nil;
_pendingCalls = nil;
if (_flowIDMap != NULL) {
@ -1010,30 +979,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
return YES;
}
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
{
RCTAssertJSThread();
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge _jsThreadUpdate:]", nil);
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (RCTModuleData *moduleData in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
if (!observer.paused) {
RCTProfileBeginFlowEvent();
[self dispatchBlock:^{
RCTProfileEndFlowEvent();
[observer didUpdateFrame:frameUpdate];
} queue:moduleData.methodQueue];
}
}
[self updateJSDisplayLinkState];
RCTProfileImmediateEvent(0, @"JS Thread Tick", displayLink.timestamp, 'g');
RCT_PROFILE_END_EVENT(0, @"objc_call", nil);
}
- (void)startProfiling
{
RCTAssertMainThread();

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
@protocol RCTBridgeModule;
@class RCTModuleData;
@interface RCTDisplayLink : NSObject
- (instancetype)init;
- (void)invalidate;
- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleData:(RCTModuleData *)moduleData;
- (void)addToRunLoop:(NSRunLoop *)runLoop;
@end

137
React/Base/RCTDisplayLink.m Normal file
View File

@ -0,0 +1,137 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTDisplayLink.h"
#import <Foundation/Foundation.h>
#import <QuartzCore/CADisplayLink.h>
#import "RCTAssert.h"
#import "RCTBridgeModule.h"
#import "RCTFrameUpdate.h"
#import "RCTModuleData.h"
#import "RCTProfile.h"
@implementation RCTDisplayLink
{
CADisplayLink *_jsDisplayLink;
NSMutableSet<RCTModuleData *> *_frameUpdateObservers;
NSRunLoop *_runLoop;
}
- (instancetype)init
{
if ((self = [super init])) {
_frameUpdateObservers = [NSMutableSet new];
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
}
return self;
}
- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleData:(RCTModuleData *)moduleData
{
if ([_frameUpdateObservers containsObject:moduleData] ||
![moduleData.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
return;
}
[_frameUpdateObservers addObject:moduleData];
// Don't access the module instance via moduleData, as this will cause deadlock
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)module;
__weak typeof(self) weakSelf = self;
observer.pauseCallback = ^{
typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop];
if (!_runLoop) {
return;
}
CFRunLoopPerformBlock(cfRunLoop, kCFRunLoopDefaultMode, ^{
[weakSelf updateJSDisplayLinkState];
});
CFRunLoopWakeUp(cfRunLoop);
};
}
- (void)addToRunLoop:(NSRunLoop *)runLoop
{
_runLoop = runLoop;
[_jsDisplayLink addToRunLoop:runLoop forMode:NSRunLoopCommonModes];
}
- (void)invalidate
{
[_jsDisplayLink invalidate];
}
- (void)assertOnRunLoop
{
RCTAssert(_runLoop == [NSRunLoop currentRunLoop],
@"This method must be called on the CADisplayLink run loop");
}
- (void)dispatchBlock:(dispatch_block_t)block
queue:(dispatch_queue_t)queue
{
if (queue == RCTJSThread) {
block();
} else if (queue) {
dispatch_async(queue, block);
}
}
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
{
[self assertOnRunLoop];
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTDisplayLink _jsThreadUpdate:]", nil);
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (RCTModuleData *moduleData in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
if (!observer.paused) {
RCTProfileBeginFlowEvent();
[self dispatchBlock:^{
RCTProfileEndFlowEvent();
[observer didUpdateFrame:frameUpdate];
} queue:moduleData.methodQueue];
}
}
[self updateJSDisplayLinkState];
RCTProfileImmediateEvent(0, @"JS Thread Tick", displayLink.timestamp, 'g');
RCT_PROFILE_END_EVENT(0, @"objc_call", nil);
}
- (void)updateJSDisplayLinkState
{
[self assertOnRunLoop];
BOOL pauseDisplayLink = YES;
for (RCTModuleData *moduleData in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
if (!observer.paused) {
pauseDisplayLink = NO;
break;
}
}
_jsDisplayLink.paused = pauseDisplayLink;
}
@end