Skip to content

Commit 725f698

Browse files
philIipfacebook-github-bot
authored andcommitted
introduce native api to access RuntimeExecutor (#42758)
Summary: Changelog: [Added][iOS] introduce native api to access RuntimeExecutor The goal of this API is to provide a safe way to access the `jsi::runtime` in bridgeless mode. The decision to limit access to the runtime in bridgeless was a conscious one - the runtime pointer is not thread-safe and its lifecycle must be managed correctly by owners. However, interacting with the runtime is an advanced use case we would want to support. Our recommended ways to access the runtime in bridgeless mode is either 1) via the RuntimeExecutor, or 2) via a C++ TurboModule. This diff introduces the API that would allow for 1). The integration consists of these parts: - Wrapper objects for RuntimeExecutor access. These are `RCTRuntimeExecution` and `RCTRuntimeExecutionWrapper`. RCTRuntimeExecutionWrapper is a C++ free wrapper of RCTRuntimeExecution to be reliably used in Swift modules. - The new API on RCTBridgeModule, following a similar pattern to other decoration APIs, and integration with our module infrastructure. Differential Revision: D53256188
1 parent 82e9a5e commit 725f698

File tree

8 files changed

+170
-0
lines changed

8 files changed

+170
-0
lines changed

packages/react-native/React/Base/RCTBridgeModule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
@class RCTModuleRegistry;
2323
@class RCTViewRegistry;
2424
@class RCTCallableJSModules;
25+
@class RCTRuntimeExecutionWrapper;
2526

2627
/**
2728
* The type of a block that is capable of sending a response to a bridged
@@ -150,6 +151,11 @@ RCT_EXTERN_C_END
150151
*/
151152
@property (nonatomic, weak, readonly) RCTBridge *bridge RCT_DEPRECATED;
152153

154+
/**
155+
* A way to reference the RuntimeExecutor.
156+
*/
157+
@property (nonatomic, readonly) RCTRuntimeExecutionWrapper *runtimeExecutionWrapper;
158+
153159
/**
154160
* This property is deprecated. This selector used to support two functionalities.
155161
*
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@class RCTRuntimeExecution;
9+
10+
NS_ASSUME_NONNULL_BEGIN
11+
12+
/**
13+
Swift friendly wrapper of RCTRuntimeExecution.
14+
*/
15+
@interface RCTRuntimeExecutionWrapper : NSObject
16+
17+
- (instancetype)init NS_UNAVAILABLE;
18+
- (instancetype)initWithRuntimeExecution:(RCTRuntimeExecution *)runtimeExecution;
19+
20+
- (RCTRuntimeExecution *)getRuntimeExecution;
21+
22+
@end
23+
24+
NS_ASSUME_NONNULL_END
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTRuntimeExecutionWrapper.h"
9+
10+
@implementation RCTRuntimeExecutionWrapper {
11+
RCTRuntimeExecution *_runtimeExecution;
12+
}
13+
14+
#pragma mark - Initializer
15+
16+
- (instancetype)initWithRuntimeExecution:(RCTRuntimeExecution *)runtimeExecution
17+
{
18+
if (self = [super init]) {
19+
_runtimeExecution = runtimeExecution;
20+
}
21+
22+
return self;
23+
}
24+
25+
#pragma mark - Public API
26+
27+
- (RCTRuntimeExecution *)getRuntimeExecution
28+
{
29+
return _runtimeExecution;
30+
}
31+
32+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <jsi/jsi.h>
9+
10+
#import <ReactCommon/CallInvoker.h>
11+
12+
NS_ASSUME_NONNULL_BEGIN
13+
14+
typedef void (^RCTJSIRuntimeHandlingBlock)(facebook::jsi::Runtime &runtime);
15+
typedef void (^RCTRuntimeExecutorBlock)(RCTJSIRuntimeHandlingBlock);
16+
17+
@interface RCTRuntimeExecution : NSObject
18+
19+
- (instancetype)init NS_UNAVAILABLE;
20+
21+
/**
22+
Initializes an object that wraps ways to access the RuntimeExecutor.
23+
24+
@param callInvoker A native-to-JS call invoker.
25+
@param runtimeExecutorBlock A block that provides thread-safe access to jsi::runtime.
26+
*/
27+
- (instancetype)initWithCallInvoker:(std::shared_ptr<facebook::react::CallInvoker>)callInvoker
28+
runtimeExecutorBlock:(RCTRuntimeExecutorBlock)runtimeExecutorBlock NS_DESIGNATED_INITIALIZER;
29+
30+
- (std::shared_ptr<facebook::react::CallInvoker>)callInvoker;
31+
32+
- (RCTRuntimeExecutorBlock)runtimeExecutorBlock;
33+
34+
@end
35+
36+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTRuntimeExecution.h"
9+
10+
@implementation RCTRuntimeExecution {
11+
std::shared_ptr<facebook::react::CallInvoker> _callInvoker;
12+
RCTRuntimeExecutorBlock _runtimeExecutorBlock;
13+
}
14+
15+
#pragma mark - Initializer
16+
17+
- (instancetype)initWithCallInvoker:(std::shared_ptr<facebook::react::CallInvoker>)callInvoker
18+
runtimeExecutorBlock:(RCTRuntimeExecutorBlock)runtimeExecutorBlock
19+
{
20+
if (self = [super init]) {
21+
_callInvoker = callInvoker;
22+
_runtimeExecutorBlock = [runtimeExecutorBlock copy];
23+
}
24+
25+
return self;
26+
}
27+
28+
#pragma mark - Public API
29+
30+
- (std::shared_ptr<facebook::react::CallInvoker>)callInvoker
31+
{
32+
return _callInvoker;
33+
}
34+
35+
- (RCTRuntimeExecutorBlock)runtimeExecutorBlock
36+
{
37+
return _runtimeExecutorBlock;
38+
}
39+
40+
@end

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
#import <React/RCTTurboModuleRegistry.h>
1616
#import <ReactCommon/RuntimeExecutor.h>
1717
#import <ReactCommon/TurboModuleBinding.h>
18+
19+
#import "RCTRuntimeExecution.h"
1820
#import "RCTTurboModule.h"
1921

22+
@class RCTTurboModuleManager;
23+
2024
@protocol RCTTurboModuleManagerDelegate <NSObject>
2125

2226
/**
@@ -50,6 +54,9 @@
5054
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
5155
__attribute((deprecated("Please make all native modules returned from this method TurboModule-compatible.")));
5256

57+
- (void)turboModuleManager:(RCTTurboModuleManager *)turboModuleManager
58+
handleRuntimeBlock:(RCTJSIRuntimeHandlingBlock)block;
59+
5360
@end
5461

5562
@interface RCTTurboModuleManager : NSObject <RCTTurboModuleRegistry>

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#import <React/RCTLog.h>
2525
#import <React/RCTModuleData.h>
2626
#import <React/RCTPerformanceLogger.h>
27+
#import <React/RCTRuntimeExecutionWrapper.h>
2728
#import <React/RCTUtils.h>
2829
#import <ReactCommon/RuntimeExecutor.h>
2930
#import <ReactCommon/TurboCxxModule.h>
@@ -676,6 +677,21 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass
676677
}
677678
}
678679

680+
if ([module respondsToSelector:@selector(runtimeExecutionWrapper)]) {
681+
__weak __typeof(self) weakSelf = self;
682+
RCTRuntimeExecution *runtimeExecution =
683+
[[RCTRuntimeExecution alloc] initWithCallInvoker:_jsInvoker
684+
runtimeExecutorBlock:^void(RCTJSIRuntimeHandlingBlock block) {
685+
__strong __typeof(self) strongSelf = weakSelf;
686+
if (strongSelf) {
687+
[strongSelf->_delegate turboModuleManager:strongSelf handleRuntimeBlock:block];
688+
}
689+
}];
690+
RCTRuntimeExecutionWrapper *runtimeExecutionWrapper =
691+
[[RCTRuntimeExecutionWrapper alloc] initWithRuntimeExecution:runtimeExecution];
692+
[(id)module setValue:runtimeExecutionWrapper forKey:@"runtimeExecutionWrapper"];
693+
}
694+
679695
/**
680696
* Some modules need their own queues, but don't provide any, so we need to create it for them.
681697
* These modules typically have the following:

packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@ - (Class)getModuleClassFromName:(const char *)name
208208
return nullptr;
209209
}
210210

211+
- (void)turboModuleManager:(RCTTurboModuleManager *)turboModuleManager
212+
handleRuntimeBlock:(RCTJSIRuntimeHandlingBlock)block;
213+
{
214+
if (_valid) {
215+
RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor();
216+
bufferedRuntimeExecutor([=](jsi::Runtime &runtime) { block(runtime); });
217+
}
218+
}
219+
211220
#pragma mark - Private
212221

213222
- (void)_start

0 commit comments

Comments
 (0)