Skip to content
Open
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 @@ -84,12 +84,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
12 changes: 2 additions & 10 deletions packages/react-native/React/Base/RCTDisplayLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,13 @@

#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;
- (void)invalidate;
- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleHolder:(id<RCTDisplayLinkModuleHolder>)moduleHolder;
- (void)registerTimingForFrameUpdates:(id<RCTFrameUpdateObserver>)module;
- (void)addToRunLoop:(NSRunLoop *)runLoop;

@end
49 changes: 10 additions & 39 deletions packages/react-native/React/Base/RCTDisplayLink.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@
#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;
NSMutableSet<id<RCTFrameUpdateObserver>> *_frameUpdateObservers;
NSRunLoop *_runLoop;
}

Expand All @@ -35,18 +33,11 @@ - (instancetype)init
return self;
}

- (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module
withModuleHolder:(id<RCTDisplayLinkModuleHolder>)moduleHolder
- (void)registerTimingForFrameUpdates:(id<RCTFrameUpdateObserver>)timing
{
if (![moduleHolder.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)] ||
[_frameUpdateObservers containsObject:moduleHolder]) {
return;
}

[_frameUpdateObservers addObject:moduleHolder];
[_frameUpdateObservers addObject:timing];

// Don't access the module instance via moduleHolder, as this will cause deadlock
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)module;
id<RCTFrameUpdateObserver> observer = timing;
__weak typeof(self) weakSelf = self;
observer.pauseCallback = ^{
typeof(self) strongSelf = weakSelf;
Expand Down Expand Up @@ -97,45 +88,26 @@ - (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;
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
[observer setPauseCallback:nil];
}
[_frameUpdateObservers removeAllObjects]; // just to be explicit

[_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 observers expect their callbacks on, so dispatch inline.
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (id<RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleHolder.instance;
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
if (!observer.paused) {
if (moduleHolder.methodQueue) {
RCTProfileBeginFlowEvent();
[self
dispatchBlock:^{
RCTProfileEndFlowEvent();
[observer didUpdateFrame:frameUpdate];
}
queue:moduleHolder.methodQueue];
} else {
[observer didUpdateFrame:frameUpdate];
}
[observer didUpdateFrame:frameUpdate];
}
}

Expand All @@ -151,8 +123,7 @@ - (void)updateJSDisplayLinkState
RCTAssertRunLoop();

BOOL pauseDisplayLink = YES;
for (id<RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleHolder.instance;
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
if (!observer.paused) {
pauseDisplayLink = NO;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ Class RCTPlatformCls(void) __attribute__((used));
Class RCTRedBoxCls(void) __attribute__((used));
Class RCTSourceCodeCls(void) __attribute__((used));
Class RCTStatusBarManagerCls(void) __attribute__((used));
Class RCTTimingCls(void) __attribute__((used));
Class RCTWebSocketModuleCls(void) __attribute__((used));
Class RCTBlobManagerCls(void) __attribute__((used));

Expand Down
4 changes: 0 additions & 4 deletions packages/react-native/React/CoreModules/CoreModulesPlugins.mm
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ Class RCTCoreModulesClassProvider(const char *name)
return RCTStatusBarManagerCls();
}

if (name == "Timing"sv) {
return RCTTimingCls();
}

if (name == "WebSocketModule"sv) {
return RCTWebSocketModuleCls();
}
Expand Down
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
85 changes: 9 additions & 76 deletions packages/react-native/React/CoreModules/RCTTiming.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,11 @@

#import "RCTTiming.h"

#import <FBReactNativeSpec/FBReactNativeSpec.h>

#import <React/RCTAssert.h>
#import <React/RCTBridge+Private.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>

#import "CoreModulesPlugins.h"

static const NSTimeInterval kMinimumSleepInterval = 1;

// These timing contants should be kept in sync with the ones in `JSTimers.js`.
Expand Down Expand Up @@ -82,7 +76,7 @@ @implementation _RCTTimingProxy {
+ (instancetype)proxyWithTarget:(id)target
{
_RCTTimingProxy *proxy = [self new];
if (proxy) {
if (proxy != nullptr) {
proxy->_target = target;
}
return proxy;
Expand All @@ -103,12 +97,9 @@ @implementation RCTTiming {
id<RCTTimingDelegate> _timingDelegate;
}

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

RCT_EXPORT_MODULE()

- (instancetype)initWithDelegate:(id<RCTTimingDelegate>)delegate
{
if (self = [super init]) {
Expand Down Expand Up @@ -165,15 +156,9 @@ - (void)dealloc
[_sleepTimer invalidate];
}

- (dispatch_queue_t)methodQueue
{
return RCTJSThread;
}

- (void)invalidate
{
[self stopTimers];
_bridge = nil;
_timingDelegate = nil;
}

Expand Down Expand Up @@ -220,7 +205,7 @@ - (void)stopTimers

- (void)startTimers
{
if ((!_bridge && !_timingDelegate) || _inBackground || ![self hasPendingTimers]) {
if ((!_timingDelegate) || _inBackground || ![self hasPendingTimers]) {
return;
}

Expand Down Expand Up @@ -259,11 +244,7 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
NSArray<NSNumber *> *sortedTimers = [[timersToCall sortedArrayUsingComparator:^(_RCTTimer *a, _RCTTimer *b) {
return [a.target compare:b.target];
}] valueForKey:@"callbackID"];
if (_bridge) {
[_bridge enqueueJSCall:@"JSTimers" method:@"callTimers" args:@[ sortedTimers ] completion:NULL];
} else {
[_timingDelegate callTimers:sortedTimers];
}
[_timingDelegate callTimers:sortedTimers];
}

for (_RCTTimer *timer in timersToCall) {
Expand All @@ -282,23 +263,19 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
if (kFrameDuration - frameElapsed >= kIdleCallbackFrameDeadline) {
NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970];
NSNumber *absoluteFrameStartMS = @((currentTimestamp - frameElapsed) * 1000);
if (_bridge) {
[_bridge enqueueJSCall:@"JSTimers" method:@"callIdleCallbacks" args:@[ absoluteFrameStartMS ] completion:NULL];
} else {
[_timingDelegate callIdleCallbacks:absoluteFrameStartMS];
}
[_timingDelegate callIdleCallbacks:absoluteFrameStartMS];
}
}

// Switch to a paused state only if we didn't call any timer this frame, so if
// in response to this timer another timer is scheduled, we don't pause and unpause
// the displaylink frivolously.
NSUInteger timerCount;
NSUInteger timerCount = 0;
@synchronized(_timers) {
timerCount = _timers.count;
}
if (_inBackground) {
if (timerCount) {
if (timerCount != 0u) {
[self scheduleSleepTimer:nextScheduledTarget];
}
} else if (!_sendIdleEvents && timersToCall.count == 0) {
Expand All @@ -318,7 +295,7 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
- (void)scheduleSleepTimer:(NSDate *)sleepTarget
{
@synchronized(self) {
if (!_sleepTimer || !_sleepTimer.valid) {
if ((_sleepTimer == nullptr) || !_sleepTimer.valid) {
_sleepTimer = [[NSTimer alloc] initWithFireDate:sleepTarget
interval:0
target:[_RCTTimingProxy proxyWithTarget:self]
Expand All @@ -343,35 +320,6 @@ - (void)timerDidFire
}
}

/**
* A method used for asynchronously creating a timer. If the timer has already expired,
* (based on the provided jsSchedulingTime) then it will be immediately invoked.
*
* There's a small difference between the time when we call
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
* it here. This is important and needs to be taken into account when
* calculating the timer's target time. We calculate this by passing in
* Date.now() from JS and then subtracting that from the current time here.
*/
RCT_EXPORT_METHOD(
createTimer : (double)callbackID duration : (NSTimeInterval)jsDuration jsSchedulingTime : (double)
jsSchedulingTime repeats : (BOOL)repeats)
{
NSNumber *callbackIdObjc = [NSNumber numberWithDouble:callbackID];
NSDate *schedulingTime = [RCTConvert NSDate:[NSNumber numberWithDouble:jsSchedulingTime]];
if (jsDuration == 0 && repeats == NO) {
// For super fast, one-off timers, just enqueue them immediately rather than waiting a frame.
if (_bridge) {
[_bridge _immediatelyCallTimer:callbackIdObjc];
} else {
[_timingDelegate immediatelyCallTimer:callbackIdObjc];
}
return;
}

[self createTimerForNextFrame:callbackIdObjc duration:jsDuration jsSchedulingTime:schedulingTime repeats:repeats];
}

/**
* A method used for synchronously creating a timer. The timer will not be invoked until the
* next frame, regardless of whether it has already expired (i.e. jsSchedulingTime is 0).
Expand Down Expand Up @@ -407,29 +355,14 @@ - (void)createTimerForNextFrame:(nonnull NSNumber *)callbackID
}
}

RCT_EXPORT_METHOD(deleteTimer : (double)timerID)
- (void)deleteTimer:(double)timerID
{
@synchronized(_timers) {
[_timers removeObjectForKey:[NSNumber numberWithDouble:timerID]];
[_timers removeObjectForKey:@(timerID)];
}
if (![self hasPendingTimers]) {
[self stopTimers];
}
}

RCT_EXPORT_METHOD(setSendIdleEvents : (BOOL)sendIdleEvents)
{
_sendIdleEvents = sendIdleEvents;
if (sendIdleEvents) {
[self startTimers];
} else if (![self hasPendingTimers]) {
[self stopTimers];
}
}

@end

Class RCTTimingCls(void)
{
return RCTTiming.class;
}
16 changes: 0 additions & 16 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -2595,22 +2595,6 @@ public final class com/facebook/react/modules/core/ReactChoreographer$Companion
public final fun initialize (Lcom/facebook/react/internal/ChoreographerProvider;)V
}

public final class com/facebook/react/modules/core/TimingModule : com/facebook/fbreact/specs/NativeTimingSpec, com/facebook/react/modules/core/JavaScriptTimerExecutor {
public static final field Companion Lcom/facebook/react/modules/core/TimingModule$Companion;
public static final field NAME Ljava/lang/String;
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/devsupport/interfaces/DevSupportManager;)V
public fun callIdleCallbacks (D)V
public fun callTimers (Lcom/facebook/react/bridge/WritableArray;)V
public fun createTimer (DDDZ)V
public fun deleteTimer (D)V
public fun emitTimeDriftWarning (Ljava/lang/String;)V
public fun invalidate ()V
public fun setSendIdleEvents (Z)V
}

public final class com/facebook/react/modules/core/TimingModule$Companion {
}

public final class com/facebook/react/modules/debug/DevSettingsModule : com/facebook/fbreact/specs/NativeDevSettingsSpec {
public static final field Companion Lcom/facebook/react/modules/debug/DevSettingsModule$Companion;
public static final field NAME Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ import kotlin.math.sign
/**
* This class is the native implementation for JS timer execution on Android. It schedules JS timers
* to be invoked on frame boundaries using [ReactChoreographer].
*
* This is used by the NativeModule [TimingModule].
*/
public open class JavaTimerManager(
private val reactApplicationContext: ReactApplicationContext,
Expand Down
Loading
Loading