mirror of
https://github.com/status-im/react-native.git
synced 2025-02-25 23:55:23 +00:00
Add the ability to pre-create the JavaScript thread with RCTJSCExecutor
Summary: This can be used to create a JavaScript thread and `JSContext` in advance, then supply them to the `RCTJSCExecutor` at creation time later. Reviewed By: javache Differential Revision: D3534553 fbshipit-source-id: 99ccf711928cd12e84c9fbe142c6d19a7af55d07
This commit is contained in:
parent
294173a427
commit
39cb110c5b
@ -25,6 +25,17 @@ RCT_EXTERN NSString *const RCTJSCThreadName;
|
|||||||
*/
|
*/
|
||||||
RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental
|
||||||
|
* May be used to pre-create the JSContext to make RCTJSCExecutor creation less costly.
|
||||||
|
* Avoid using this; it's experimental and is not likely to be supported long-term.
|
||||||
|
*/
|
||||||
|
@interface RCTJSContextProvider : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses a JavaScriptCore context as the execution engine.
|
* Uses a JavaScriptCore context as the execution engine.
|
||||||
*/
|
*/
|
||||||
@ -43,6 +54,11 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
|||||||
*/
|
*/
|
||||||
- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary;
|
- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass a RCTJSContextProvider object to use an NSThread/JSContext pair that have already been created.
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a NSError from a JSError object.
|
* Create a NSError from a JSError object.
|
||||||
*
|
*
|
||||||
|
@ -74,6 +74,18 @@ struct RandomAccessBundleStartupCode {
|
|||||||
@implementation RCTCookieMap @end
|
@implementation RCTCookieMap @end
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct RCTJSContextData {
|
||||||
|
BOOL useCustomJSCLibrary;
|
||||||
|
NSThread *javaScriptThread;
|
||||||
|
JSContext *context;
|
||||||
|
RCTJSCWrapper *jscWrapper;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface RCTJSContextProvider ()
|
||||||
|
/** May only be called once, or deadlock will result. */
|
||||||
|
- (RCTJSContextData)data;
|
||||||
|
@end
|
||||||
|
|
||||||
@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
|
@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) JSContext *context;
|
@property (nonatomic, strong, readonly) JSContext *context;
|
||||||
@ -271,6 +283,21 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSThread *newJavaScriptThread(void)
|
||||||
|
{
|
||||||
|
NSThread *javaScriptThread = [[NSThread alloc] initWithTarget:[RCTJSCExecutor class]
|
||||||
|
selector:@selector(runRunLoopThread)
|
||||||
|
object:nil];
|
||||||
|
javaScriptThread.name = RCTJSCThreadName;
|
||||||
|
if ([javaScriptThread respondsToSelector:@selector(setQualityOfService:)]) {
|
||||||
|
[javaScriptThread setQualityOfService:NSOperationQualityOfServiceUserInteractive];
|
||||||
|
} else {
|
||||||
|
javaScriptThread.threadPriority = [NSThread mainThread].threadPriority;
|
||||||
|
}
|
||||||
|
[javaScriptThread start];
|
||||||
|
return javaScriptThread;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setBridge:(RCTBridge *)bridge
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
@ -287,21 +314,22 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
|||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_useCustomJSCLibrary = useCustomJSCLibrary;
|
_useCustomJSCLibrary = useCustomJSCLibrary;
|
||||||
_valid = YES;
|
_valid = YES;
|
||||||
|
_javaScriptThread = newJavaScriptThread();
|
||||||
_javaScriptThread = [[NSThread alloc] initWithTarget:[self class]
|
|
||||||
selector:@selector(runRunLoopThread)
|
|
||||||
object:nil];
|
|
||||||
_javaScriptThread.name = RCTJSCThreadName;
|
|
||||||
|
|
||||||
if ([_javaScriptThread respondsToSelector:@selector(setQualityOfService:)]) {
|
|
||||||
[_javaScriptThread setQualityOfService:NSOperationQualityOfServiceUserInteractive];
|
|
||||||
} else {
|
|
||||||
_javaScriptThread.threadPriority = [NSThread mainThread].threadPriority;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[_javaScriptThread start];
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithJSContextProvider:(RCTJSContextProvider *)JSContextProvider
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
const RCTJSContextData data = JSContextProvider.data;
|
||||||
|
_useCustomJSCLibrary = data.useCustomJSCLibrary;
|
||||||
|
_valid = YES;
|
||||||
|
_javaScriptThread = data.javaScriptThread;
|
||||||
|
_jscWrapper = data.jscWrapper;
|
||||||
|
_context = [[RCTJavaScriptContext alloc] initWithJSContext:data.context onThread:_javaScriptThread];
|
||||||
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,25 +369,24 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSContext *context = nil;
|
||||||
|
if (self->_jscWrapper) {
|
||||||
|
RCTAssert(self->_context != nil, @"If wrapper was pre-initialized, context should be too");
|
||||||
|
context = self->_context.context;
|
||||||
|
} else {
|
||||||
[self->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary];
|
[self->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary];
|
||||||
self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary);
|
self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary);
|
||||||
[self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary];
|
[self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary];
|
||||||
|
|
||||||
RCTAssert(self->_context == nil, @"Didn't expect to set up twice");
|
RCTAssert(self->_context == nil, @"Didn't expect to set up twice");
|
||||||
JSContext *context = [self->_jscWrapper->JSContext new];
|
context = [self->_jscWrapper->JSContext new];
|
||||||
self->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:self->_javaScriptThread];
|
self->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:self->_javaScriptThread];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification
|
||||||
object:context];
|
object:context];
|
||||||
|
|
||||||
if (self->_jscWrapper->configureJSContextForIOS != NULL) {
|
configureCacheOnContext(context, self->_jscWrapper);
|
||||||
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
|
installBasicSynchronousHooksOnContext(context);
|
||||||
RCTAssert(cachesPath != nil, @"cachesPath should not be nil");
|
|
||||||
if (cachesPath) {
|
|
||||||
self->_jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[self class] installSynchronousHooksOnContext:context];
|
|
||||||
|
|
||||||
__weak RCTJSCExecutor *weakSelf = self;
|
__weak RCTJSCExecutor *weakSelf = self;
|
||||||
|
|
||||||
@ -430,7 +457,20 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)installSynchronousHooksOnContext:(JSContext *)context
|
/** If configureJSContextForIOS is available on jscWrapper, calls it with the correct parameters. */
|
||||||
|
static void configureCacheOnContext(JSContext *context, RCTJSCWrapper *jscWrapper)
|
||||||
|
{
|
||||||
|
if (jscWrapper->configureJSContextForIOS != NULL) {
|
||||||
|
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
|
||||||
|
RCTAssert(cachesPath != nil, @"cachesPath should not be nil");
|
||||||
|
if (cachesPath) {
|
||||||
|
jscWrapper->configureJSContextForIOS(context.JSGlobalContextRef, [cachesPath UTF8String]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Installs synchronous hooks that don't require a weak reference back to the RCTJSCExecutor. */
|
||||||
|
static void installBasicSynchronousHooksOnContext(JSContext *context)
|
||||||
{
|
{
|
||||||
context[@"noop"] = ^{};
|
context[@"noop"] = ^{};
|
||||||
context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) {
|
context[@"nativeLoggingHook"] = ^(NSString *message, NSNumber *logLevel) {
|
||||||
@ -889,3 +929,46 @@ RCT_EXPORT_METHOD(setContextName:(nonnull NSString *)name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@implementation RCTJSContextProvider
|
||||||
|
{
|
||||||
|
dispatch_semaphore_t _semaphore;
|
||||||
|
BOOL _useCustomJSCLibrary;
|
||||||
|
NSThread *_javaScriptThread;
|
||||||
|
JSContext *_context;
|
||||||
|
RCTJSCWrapper *_jscWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
_semaphore = dispatch_semaphore_create(0);
|
||||||
|
_useCustomJSCLibrary = useCustomJSCLibrary;
|
||||||
|
_javaScriptThread = newJavaScriptThread();
|
||||||
|
[self performSelector:@selector(_createContext) onThread:_javaScriptThread withObject:nil waitUntilDone:NO];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_createContext
|
||||||
|
{
|
||||||
|
_jscWrapper = RCTJSCWrapperCreate(_useCustomJSCLibrary);
|
||||||
|
_context = [_jscWrapper->JSContext new];
|
||||||
|
configureCacheOnContext(_context, _jscWrapper);
|
||||||
|
installBasicSynchronousHooksOnContext(_context);
|
||||||
|
dispatch_semaphore_signal(_semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (RCTJSContextData)data
|
||||||
|
{
|
||||||
|
// Be sure this method is only called once, otherwise it will hang here forever:
|
||||||
|
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
|
||||||
|
return {
|
||||||
|
.useCustomJSCLibrary = _useCustomJSCLibrary,
|
||||||
|
.javaScriptThread = _javaScriptThread,
|
||||||
|
.context = _context,
|
||||||
|
.jscWrapper = _jscWrapper,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user