Skip to content

Commit

Permalink
Pause JS DisplayLink if nothing to process.
Browse files Browse the repository at this point in the history
Reviewed By: @jspahrsummers

Differential Revision: D2489107
  • Loading branch information
alexeylang authored and facebook-github-bot-4 committed Sep 29, 2015
1 parent 4c74f01 commit e727fc8
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 10 deletions.
30 changes: 29 additions & 1 deletion React/Base/RCTBatchedBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,15 @@ - (NSString *)moduleConfig
config[moduleData.name] = moduleData.config;
if ([moduleData.instance conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
[_frameUpdateObservers addObject:moduleData];

id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
__weak typeof(self) weakSelf = self;
__weak typeof(_javaScriptExecutor) weakJavaScriptExecutor = _javaScriptExecutor;
observer.pauseCallback = ^{
[weakJavaScriptExecutor executeBlockOnJavaScriptQueue:^{
[weakSelf updateJSDisplayLinkState];
}];
};
}
}

Expand All @@ -317,6 +326,23 @@ - (NSString *)moduleConfig
}, NULL);
}

- (void)updateJSDisplayLinkState
{
RCTAssertJSThread();

BOOL pauseDisplayLink = ![_scheduledCallbacks count] && ![_scheduledCalls count];
if (pauseDisplayLink) {
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
{
Expand Down Expand Up @@ -620,6 +646,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg
} else {
[strongSelf->_scheduledCalls addObject:call];
}
[strongSelf updateJSDisplayLinkState];

RCTProfileEndEvent(0, @"objc_call", call);
}];
Expand Down Expand Up @@ -804,7 +831,7 @@ - (void)_jsThreadUpdate:(CADisplayLink *)displayLink
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (RCTModuleData *moduleData in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
if (![observer respondsToSelector:@selector(isPaused)] || !observer.paused) {
if (!observer.paused) {
RCT_IF_DEV(NSString *name = [NSString stringWithFormat:@"[%@ didUpdateFrame:%f]", observer, displayLink.timestamp];)
RCTProfileBeginFlowEvent();

Expand Down Expand Up @@ -833,6 +860,7 @@ - (void)_jsThreadUpdate:(CADisplayLink *)displayLink
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
method:@"processBatch"
arguments:@[[calls valueForKey:@"js_args"]]];
[self updateJSDisplayLinkState];
}

RCTProfileEndEvent(0, @"objc_call", nil);
Expand Down
16 changes: 14 additions & 2 deletions React/Base/RCTEventDispatcher.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,30 @@ @implementation RCTEventDispatcher

@synthesize bridge = _bridge;
@synthesize paused = _paused;
@synthesize pauseCallback = _pauseCallback;

RCT_EXPORT_MODULE()

- (instancetype)init
{
if ((self = [super init])) {
_paused = YES;
_eventQueue = [NSMutableDictionary new];
_eventQueueLock = [NSLock new];
}
return self;
}

- (void)setPaused:(BOOL)paused
{
if (_paused != paused) {
_paused = paused;
if (_pauseCallback) {
_pauseCallback();
}
}
}

- (void)sendAppEventWithName:(NSString *)name body:(id)body
{
[_bridge enqueueJSCall:@"RCTNativeAppEventEmitter.emit"
Expand Down Expand Up @@ -169,7 +181,7 @@ - (void)sendEvent:(id<RCTEvent>)event
}

_eventQueue[eventID] = event;
_paused = NO;
self.paused = NO;

[_eventQueueLock unlock];
}
Expand Down Expand Up @@ -202,7 +214,7 @@ - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
[_eventQueueLock lock];
NSDictionary *eventQueue = _eventQueue;
_eventQueue = [NSMutableDictionary new];
_paused = YES;
self.paused = YES;
[_eventQueueLock unlock];

for (id<RCTEvent> event in eventQueue.allValues) {
Expand Down
10 changes: 7 additions & 3 deletions React/Base/RCTFrameUpdate.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@
*/
- (void)didUpdateFrame:(RCTFrameUpdate *)update;

@optional

/**
* Synthesize and set to true to pause the calls to -[didUpdateFrame:]
*/
@property (nonatomic, assign, getter=isPaused) BOOL paused;
@property (nonatomic, readonly, getter=isPaused) BOOL paused;

/**
* Callback for pause/resume observer.
* Observer should call it when paused property is changed.
*/
@property (nonatomic, copy) dispatch_block_t pauseCallback;

@end
15 changes: 13 additions & 2 deletions React/Modules/RCTTiming.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ @implementation RCTTiming

@synthesize bridge = _bridge;
@synthesize paused = _paused;
@synthesize pauseCallback = _pauseCallback;

RCT_EXPORT_MODULE()

Expand Down Expand Up @@ -120,7 +121,7 @@ - (void)invalidate

- (void)stopTimers
{
_paused = YES;
self.paused = YES;
}

- (void)startTimers
Expand All @@ -129,7 +130,17 @@ - (void)startTimers
return;
}

_paused = NO;
self.paused = NO;
}

- (void)setPaused:(BOOL)paused
{
if (_paused != paused) {
_paused = paused;
if (_pauseCallback) {
_pauseCallback();
}
}
}

- (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
Expand Down
15 changes: 13 additions & 2 deletions React/Views/RCTNavigator.m
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ @implementation RCTNavigator
}

@synthesize paused = _paused;
@synthesize pauseCallback = _pauseCallback;

- (instancetype)initWithBridge:(RCTBridge *)bridge
{
Expand Down Expand Up @@ -321,6 +322,16 @@ - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
}
}

- (void)setPaused:(BOOL)paused
{
if (_paused != paused) {
_paused = paused;
if (_pauseCallback) {
_pauseCallback();
}
}
}

- (void)dealloc
{
_navigationController.delegate = nil;
Expand Down Expand Up @@ -355,14 +366,14 @@ - (void)navigationController:(UINavigationController *)navigationController
_dummyView.frame = (CGRect){{destination, 0}, CGSizeZero};
_currentlyTransitioningFrom = indexOfFrom;
_currentlyTransitioningTo = indexOfTo;
_paused = NO;
self.paused = NO;
}
completion:^(__unused id<UIViewControllerTransitionCoordinatorContext> context) {
[weakSelf freeLock];
_currentlyTransitioningFrom = 0;
_currentlyTransitioningTo = 0;
_dummyView.frame = CGRectZero;
_paused = YES;
self.paused = YES;
// Reset the parallel position tracker
}];
}
Expand Down

0 comments on commit e727fc8

Please sign in to comment.