Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions packages/react-native/React/Base/RCTBridge+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ RCT_EXTERN void RCTRegisterModule(Class);
*/
- (void)start;

/**
* Used by RCTModuleData to register the module for frame updates after it is
* lazily initialized.
*/
- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module withModuleData:(RCTModuleData *)moduleData;

/**
* Dispatch work to a module's queue - this is also supports the fake RCTJSThread
* queue. Exposed for the RCTProfiler
Expand Down
15 changes: 2 additions & 13 deletions packages/react-native/React/Base/RCTDisplayLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,12 @@

#import <Foundation/Foundation.h>

@protocol RCTBridgeModule;
@class RCTModuleData;

@protocol RCTDisplayLinkModuleHolder
- (id<RCTBridgeModule>)instance;
- (Class)moduleClass;
- (dispatch_queue_t)methodQueue;
@end
@protocol RCTFrameUpdateObserver;

@interface RCTDisplayLink : NSObject

- (instancetype)init;
- (instancetype)initWithFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
- (void)invalidate;
- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleHolder:(id<RCTDisplayLinkModuleHolder>)moduleHolder
__attribute__((deprecated(
"registerModuleForFrameUpdates is part of the legacy architecture and will be removed in a future React Native release.")));
- (void)addToRunLoop:(NSRunLoop *)runLoop;

@end
124 changes: 33 additions & 91 deletions packages/react-native/React/Base/RCTDisplayLink.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,50 @@
#import <QuartzCore/CADisplayLink.h>

#import "RCTAssert.h"
#import "RCTBridgeModule.h"
#import "RCTFrameUpdate.h"
#import "RCTModuleData.h"
#import "RCTProfile.h"

#define RCTAssertRunLoop() \
RCTAssert(_runLoop == [NSRunLoop currentRunLoop], @"This method must be called on the CADisplayLink run loop")

@implementation RCTDisplayLink {
CADisplayLink *_jsDisplayLink;
NSMutableSet<id<RCTDisplayLinkModuleHolder>> *_frameUpdateObservers;
id<RCTFrameUpdateObserver> _frameUpdateObserver;
NSRunLoop *_runLoop;
}

- (instancetype)init
- (instancetype)initWithFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
{
if ((self = [super init])) {
_frameUpdateObservers = [NSMutableSet new];
_frameUpdateObserver = observer;
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
}

return self;
}
__weak typeof(self) weakSelf = self;
observer.pauseCallback = ^{
typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}

- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleHolder:(id<RCTDisplayLinkModuleHolder>)moduleHolder
{
if (![moduleHolder.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)] ||
[_frameUpdateObservers containsObject:moduleHolder]) {
return;
}
CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop];
if (!cfRunLoop) {
return;
}

[_frameUpdateObservers addObject:moduleHolder];

// Don't access the module instance via moduleHolder, 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 (!cfRunLoop) {
return;
}

if ([NSRunLoop currentRunLoop] == strongSelf->_runLoop) {
[weakSelf updateJSDisplayLinkState];
} else {
CFRunLoopPerformBlock(cfRunLoop, kCFRunLoopDefaultMode, ^{
@autoreleasepool {
[weakSelf updateJSDisplayLinkState];
}
});
CFRunLoopWakeUp(cfRunLoop);
}
};

// Assuming we're paused right now, we only need to update the display link's state
// when the new observer is not paused. If it not paused, the observer will immediately
// start receiving updates anyway.
if (![observer isPaused] && _runLoop) {
CFRunLoopPerformBlock([_runLoop getCFRunLoop], kCFRunLoopDefaultMode, ^{
@autoreleasepool {
[self updateJSDisplayLinkState];
if ([NSRunLoop currentRunLoop] == strongSelf->_runLoop) {
[weakSelf updateJSDisplayLinkState];
} else {
CFRunLoopPerformBlock(cfRunLoop, kCFRunLoopDefaultMode, ^{
@autoreleasepool {
[weakSelf updateJSDisplayLinkState];
}
});
CFRunLoopWakeUp(cfRunLoop);
}
});
};
}

return self;
}

- (void)addToRunLoop:(NSRunLoop *)runLoop
Expand All @@ -96,47 +70,24 @@ - (void)dealloc

- (void)invalidate
{
// ensure observer callbacks do not hold a reference to weak self via pauseCallback
for (id<RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleHolder.instance;
[observer setPauseCallback:nil];
}
[_frameUpdateObservers removeAllObjects]; // just to be explicit
// ensure the observer callback does not hold a reference to weak self via pauseCallback
[_frameUpdateObserver setPauseCallback:nil];
_frameUpdateObserver = nil;

[_jsDisplayLink invalidate];
}

- (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
{
RCTAssertRunLoop();

RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTDisplayLink _jsThreadUpdate:]", nil);

// This always runs on the JS thread run loop, which is the queue the frame
// update observer expects its callbacks on, so dispatch inline.
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (id<RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleHolder.instance;
if (!observer.paused) {
if (moduleHolder.methodQueue) {
RCTProfileBeginFlowEvent();
[self
dispatchBlock:^{
RCTProfileEndFlowEvent();
[observer didUpdateFrame:frameUpdate];
}
queue:moduleHolder.methodQueue];
} else {
[observer didUpdateFrame:frameUpdate];
}
}
if (!_frameUpdateObserver.paused) {
[_frameUpdateObserver didUpdateFrame:frameUpdate];
}

[self updateJSDisplayLinkState];
Expand All @@ -150,16 +101,7 @@ - (void)updateJSDisplayLinkState
{
RCTAssertRunLoop();

BOOL pauseDisplayLink = YES;
for (id<RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleHolder.instance;
if (!observer.paused) {
pauseDisplayLink = NO;
break;
}
}

_jsDisplayLink.paused = pauseDisplayLink;
_jsDisplayLink.paused = _frameUpdateObserver == nil || _frameUpdateObserver.paused;
}

@end
3 changes: 1 addition & 2 deletions packages/react-native/React/CoreModules/RCTTiming.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#import <Foundation/Foundation.h>

#import <React/RCTBridgeModule.h>
#import <React/RCTFrameUpdate.h>
#import <React/RCTInitializing.h>
#import <React/RCTInvalidating.h>
Expand All @@ -22,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN

@end

@interface RCTTiming : NSObject <RCTBridgeModule, RCTInvalidating, RCTFrameUpdateObserver, RCTInitializing>
@interface RCTTiming : NSObject <RCTInvalidating, RCTFrameUpdateObserver, RCTInitializing>

- (instancetype)initWithDelegate:(id<RCTTimingDelegate>)delegate;
- (void)createTimerForNextFrame:(NSNumber *)callbackID
Expand Down
Loading
Loading