[ReactNative] Move VSync bound events to JS thread

This commit is contained in:
Tadeu Zagallo 2015-04-22 07:03:55 -07:00
parent 462224727a
commit 3595b79ec3
5 changed files with 91 additions and 50 deletions

View File

@ -175,6 +175,11 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
}); });
} }
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
dispatch_async(dispatch_get_main_queue(), block);
}
- (void)invalidate - (void)invalidate
{ {
_socket.delegate = nil; _socket.delegate = nil;

View File

@ -694,7 +694,7 @@ static NSDictionary *RCTLocalModulesConfig()
@interface RCTDisplayLink : NSObject <RCTInvalidating> @interface RCTDisplayLink : NSObject <RCTInvalidating>
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; - (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector NS_DESIGNATED_INITIALIZER;
@end @end
@ -708,14 +708,16 @@ static NSDictionary *RCTLocalModulesConfig()
{ {
__weak RCTBridge *_bridge; __weak RCTBridge *_bridge;
CADisplayLink *_displayLink; CADisplayLink *_displayLink;
SEL _selector;
} }
- (instancetype)initWithBridge:(RCTBridge *)bridge - (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector
{ {
if ((self = [super init])) { if ((self = [super init])) {
_bridge = bridge; _bridge = bridge;
_selector = selector;
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)]; _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
} }
return self; return self;
} }
@ -735,7 +737,10 @@ static NSDictionary *RCTLocalModulesConfig()
- (void)_update:(CADisplayLink *)displayLink - (void)_update:(CADisplayLink *)displayLink
{ {
[_bridge _update:displayLink]; #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[_bridge performSelector:_selector withObject:displayLink];
#pragma clang diagnostic pop
} }
@end @end
@ -770,6 +775,7 @@ static NSDictionary *RCTLocalModulesConfig()
NSURL *_bundleURL; NSURL *_bundleURL;
RCTBridgeModuleProviderBlock _moduleProvider; RCTBridgeModuleProviderBlock _moduleProvider;
RCTDisplayLink *_displayLink; RCTDisplayLink *_displayLink;
RCTDisplayLink *_vsyncDisplayLink;
NSMutableSet *_frameUpdateObservers; NSMutableSet *_frameUpdateObservers;
NSMutableArray *_scheduledCalls; NSMutableArray *_scheduledCalls;
RCTSparseArray *_scheduledCallbacks; RCTSparseArray *_scheduledCallbacks;
@ -799,11 +805,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_latestJSExecutor = _javaScriptExecutor; _latestJSExecutor = _javaScriptExecutor;
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL); _methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL);
_displayLink = [[RCTDisplayLink alloc] initWithBridge:self];
_frameUpdateObservers = [[NSMutableSet alloc] init]; _frameUpdateObservers = [[NSMutableSet alloc] init];
_scheduledCalls = [[NSMutableArray alloc] init]; _scheduledCalls = [[NSMutableArray alloc] init];
_scheduledCallbacks = [[RCTSparseArray alloc] init]; _scheduledCallbacks = [[RCTSparseArray alloc] init];
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
_displayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_jsThreadUpdate:)];
}];
_vsyncDisplayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_mainThreadUpdate:)];
// Register passed-in module instances // Register passed-in module instances
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
for (id<RCTBridgeModule> module in _moduleProvider ? _moduleProvider() : nil) { for (id<RCTBridgeModule> module in _moduleProvider ? _moduleProvider() : nil) {
@ -1008,6 +1018,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_javaScriptExecutor = nil; _javaScriptExecutor = nil;
[_displayLink invalidate]; [_displayLink invalidate];
[_vsyncDisplayLink invalidate];
_frameUpdateObservers = nil; _frameUpdateObservers = nil;
// Invalidate modules // Invalidate modules
@ -1294,9 +1305,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return YES; return YES;
} }
- (void)_update:(CADisplayLink *)displayLink - (void)_jsThreadUpdate:(CADisplayLink *)displayLink
{ {
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
RCTProfileBeginEvent(); RCTProfileBeginEvent();
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink]; RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
@ -1306,13 +1317,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
} }
} }
[self _runScheduledCalls];
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
}
- (void)_runScheduledCalls
{
#if BATCHED_BRIDGE #if BATCHED_BRIDGE
NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls]; NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls];
@ -1330,6 +1334,13 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
} }
#endif #endif
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
}
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
{
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
} }
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer - (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer

View File

@ -42,6 +42,13 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
- (void)injectJSONText:(NSString *)script - (void)injectJSONText:(NSString *)script
asGlobalObjectNamed:(NSString *)objectName asGlobalObjectNamed:(NSString *)objectName
callback:(RCTJavaScriptCompleteBlock)onComplete; callback:(RCTJavaScriptCompleteBlock)onComplete;
/**
* Enqueue a block to run in the executors JS thread. Fallback to `dispatch_async`
* on the main queue if the executor doesn't own a thread.
*/
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block;
@end @end
static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID"; static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID";

View File

@ -36,6 +36,7 @@ NSDictionary *RCTProfileInfo;
NSUInteger RCTProfileEventID = 0; NSUInteger RCTProfileEventID = 0;
NSMutableDictionary *RCTProfileOngoingEvents; NSMutableDictionary *RCTProfileOngoingEvents;
NSTimeInterval RCTProfileStartTime; NSTimeInterval RCTProfileStartTime;
NSLock *_RCTProfileLock;
#pragma mark - Macros #pragma mark - Macros
@ -51,6 +52,11 @@ if (!RCTProfileIsProfiling()) { \
return __VA_ARGS__; \ return __VA_ARGS__; \
} }
#define RCTProfileLock(...) \
[_RCTProfileLock lock]; \
__VA_ARGS__ \
[_RCTProfileLock unlock]
#pragma mark - Private Helpers #pragma mark - Private Helpers
NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp) NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp)
@ -66,7 +72,6 @@ NSString *RCTProfileMemory(vm_size_t memory)
NSDictionary *RCTProfileGetMemoryUsage(void) NSDictionary *RCTProfileGetMemoryUsage(void)
{ {
CHECK(@{});
struct task_basic_info info; struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info); mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(), kern_return_t kerr = task_info(mach_task_self(),
@ -88,66 +93,81 @@ NSDictionary *RCTProfileGetMemoryUsage(void)
BOOL RCTProfileIsProfiling(void) BOOL RCTProfileIsProfiling(void)
{ {
return RCTProfileInfo != nil; RCTProfileLock(
BOOL profiling = RCTProfileInfo != nil;
);
return profiling;
} }
void RCTProfileInit(void) void RCTProfileInit(void)
{ {
RCTProfileStartTime = CACurrentMediaTime(); static dispatch_once_t onceToken;
RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init]; dispatch_once(&onceToken, ^{
RCTProfileInfo = @{ _RCTProfileLock = [[NSLock alloc] init];
RCTProfileTraceEvents: [[NSMutableArray alloc] init], });
RCTProfileSamples: [[NSMutableArray alloc] init], RCTProfileLock(
}; RCTProfileStartTime = CACurrentMediaTime();
RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init];
RCTProfileInfo = @{
RCTProfileTraceEvents: [[NSMutableArray alloc] init],
RCTProfileSamples: [[NSMutableArray alloc] init],
};
);
} }
NSString *RCTProfileEnd(void) NSString *RCTProfileEnd(void)
{ {
NSString *log = RCTJSONStringify(RCTProfileInfo, NULL); RCTProfileLock(
RCTProfileEventID = 0; NSString *log = RCTJSONStringify(RCTProfileInfo, NULL);
RCTProfileInfo = nil; RCTProfileEventID = 0;
RCTProfileOngoingEvents = nil; RCTProfileInfo = nil;
RCTProfileOngoingEvents = nil;
);
return log; return log;
} }
NSNumber *_RCTProfileBeginEvent(void) NSNumber *_RCTProfileBeginEvent(void)
{ {
CHECK(@0); CHECK(@0);
NSNumber *eventID = @(++RCTProfileEventID); RCTProfileLock(
RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime()); NSNumber *eventID = @(++RCTProfileEventID);
RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime());
);
return eventID; return eventID;
} }
void _RCTProfileEndEvent(NSNumber *eventID, NSString *name, NSString *categories, id args) void _RCTProfileEndEvent(NSNumber *eventID, NSString *name, NSString *categories, id args)
{ {
CHECK(); CHECK();
NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID]; RCTProfileLock(
if (!startTimestamp) { NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID];
return; if (startTimestamp) {
} NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime());
NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime()); RCTProfileAddEvent(RCTProfileTraceEvents,
@"name": name,
RCTProfileAddEvent(RCTProfileTraceEvents, @"cat": categories,
@"name": name, @"ph": @"X",
@"cat": categories, @"ts": startTimestamp,
@"ph": @"X", @"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue),
@"ts": startTimestamp, @"args": args ?: @[],
@"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue), );
@"args": args ?: @[], [RCTProfileOngoingEvents removeObjectForKey:eventID];
}
); );
[RCTProfileOngoingEvents removeObjectForKey:eventID];
} }
void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString *scope) void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString *scope)
{ {
CHECK(); CHECK();
RCTProfileAddEvent(RCTProfileTraceEvents, RCTProfileLock(
@"name": name, RCTProfileAddEvent(RCTProfileTraceEvents,
@"ts": RCTProfileTimestamp(timestamp), @"name": name,
@"scope": scope, @"ts": RCTProfileTimestamp(timestamp),
@"ph": @"i", @"scope": scope,
@"args": RCTProfileGetMemoryUsage(), @"ph": @"i",
@"args": RCTProfileGetMemoryUsage(),
);
); );
} }

View File

@ -142,8 +142,6 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
- (void)didUpdateFrame:(RCTFrameUpdate *)update - (void)didUpdateFrame:(RCTFrameUpdate *)update
{ {
RCTAssertMainThread();
NSMutableArray *timersToCall = [[NSMutableArray alloc] init]; NSMutableArray *timersToCall = [[NSMutableArray alloc] init];
for (RCTTimer *timer in _timers.allObjects) { for (RCTTimer *timer in _timers.allObjects) {
if ([timer updateFoundNeedsJSUpdate]) { if ([timer updateFoundNeedsJSUpdate]) {