Skip to content

Commit 61a10db

Browse files
authored
feat(Worklets): dispatch to Worklet Runtimes from id (#8991)
## Summary Required by - #8673 Adding APIs to dispatch to a Worklet Runtime from JS only knowing its unique ID - `runOnRuntimeSyncWithId` - `scheduleOnRuntimeWithId` It's handy for situations where it's difficult to pass the WorkletRuntime host objects but the runtime id is known. You can also dispatch to the UI Runtime that way. The API is virtually the same as for `runOnRuntimeSync` and `scheduleOnRuntime`, respectively, only that it takes a `number` instead of a `WorkletRuntime` as first argument. I also exported `UIRuntimeId` const so the id doesn't have to be hard-coded. I amended the runtime ids in `RuntimeData.h` to make sure that the runtime id for the UI runtime has the same value as `RuntimeKind.UI`. ## Test plan I added a new runtime test suite, it works both in Bundle Mode and outside of it.
1 parent fd07ac6 commit 61a10db

19 files changed

Lines changed: 990 additions & 13 deletions

File tree

apps/common-app/src/apps/reanimated/examples/RuntimeTests/RuntimeTestsExample.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export default function RuntimeTestsExample() {
6161
require('./tests/runtimes/scheduleOnRuntime.test');
6262
require('./tests/runtimes/scheduleOnUI.test');
6363
require('./tests/runtimes/runOnRuntimeSync.test');
64+
require('./tests/runtimes/runOnRuntimeSyncWithId.test');
65+
require('./tests/runtimes/scheduleOnRuntimeWithId.test');
6466
},
6567
},
6668
{
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
import {
2+
createWorkletRuntime,
3+
scheduleOnRN,
4+
scheduleOnRuntime,
5+
runOnRuntimeSyncWithId,
6+
scheduleOnUI,
7+
UIRuntimeId,
8+
} from 'react-native-worklets';
9+
import { describe, expect, notify, test, waitForNotification, beforeEach } from '../../ReJest/RuntimeTestsApi';
10+
11+
const PASS_NOTIFICATION = 'PASS';
12+
const FAIL_NOTIFICATION = 'FAIL';
13+
14+
describe('runOnRuntimeSyncWithId', () => {
15+
let value = 0;
16+
let reason = '';
17+
18+
const callbackPass = (num: number) => {
19+
value = num;
20+
notify(PASS_NOTIFICATION);
21+
};
22+
23+
const callbackFail = (rea: string) => {
24+
reason = rea;
25+
notify(FAIL_NOTIFICATION);
26+
};
27+
28+
const workletRuntime1 = createWorkletRuntime({ name: 'test1' });
29+
const workletRuntime2 = createWorkletRuntime({ name: 'test2' });
30+
31+
test('setup beforeEach', () => {
32+
// TODO: there's a bug in ReJest and beforeEach has to be registered
33+
// inside a test case.
34+
beforeEach(() => {
35+
value = 0;
36+
reason = '';
37+
});
38+
});
39+
40+
test('from RN Runtime to UI Runtime', () => {
41+
const result = runOnRuntimeSyncWithId(UIRuntimeId, () => {
42+
'worklet';
43+
return 42;
44+
});
45+
46+
expect(result).toBe(42);
47+
});
48+
49+
test('from RN Runtime to Worker Runtime', () => {
50+
const result = runOnRuntimeSyncWithId(workletRuntime1.runtimeId, () => {
51+
'worklet';
52+
return 42;
53+
});
54+
55+
expect(result).toBe(42);
56+
});
57+
58+
test('from RN Runtime to non-existing Runtime', async () => {
59+
const fun = () =>
60+
runOnRuntimeSyncWithId(9999, () => {
61+
'worklet';
62+
return 42;
63+
});
64+
65+
await expect(fun).toThrow('[Worklets] runOnRuntimeSyncWithId: no worklet runtime found for id 9999');
66+
});
67+
68+
if (globalThis._WORKLETS_BUNDLE_MODE_ENABLED) {
69+
test('from UI Runtime to UI Runtime', async () => {
70+
value = 0;
71+
scheduleOnUI(() => {
72+
'worklet';
73+
// @ts-expect-error TODO: fix RemoteFunction re-serialization.
74+
const remoteFunction = callbackPass.__remoteFunction as typeof callbackPass;
75+
const result = runOnRuntimeSyncWithId(UIRuntimeId, () => {
76+
'worklet';
77+
return 42;
78+
});
79+
scheduleOnRN(remoteFunction, result);
80+
});
81+
await waitForNotification(PASS_NOTIFICATION);
82+
expect(value).toBe(42);
83+
});
84+
85+
test('from UI Runtime to Worker Runtime', async () => {
86+
scheduleOnUI(() => {
87+
'worklet';
88+
// @ts-expect-error TODO: fix RemoteFunction re-serialization.
89+
const remoteFunction = callbackPass.__remoteFunction as typeof callbackPass;
90+
91+
const result = runOnRuntimeSyncWithId(workletRuntime1.runtimeId, () => {
92+
'worklet';
93+
return 42;
94+
});
95+
scheduleOnRN(remoteFunction, result);
96+
});
97+
await waitForNotification(PASS_NOTIFICATION);
98+
expect(value).toBe(42);
99+
});
100+
101+
test('from UI Runtime to non-existing Runtime', async () => {
102+
scheduleOnUI(() => {
103+
'worklet';
104+
// @ts-expect-error TODO: fix RemoteFunction re-serialization.
105+
const remoteFunction = callbackPass.__remoteFunction as typeof callbackPass;
106+
try {
107+
const result = runOnRuntimeSyncWithId(9999, () => {
108+
'worklet';
109+
return 42;
110+
});
111+
scheduleOnRN(remoteFunction, result);
112+
} catch (error) {
113+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
114+
}
115+
});
116+
117+
await waitForNotification(FAIL_NOTIFICATION);
118+
expect(reason).toBe('[Worklets] runOnRuntimeSyncWithId: no worklet runtime found for id 9999');
119+
});
120+
} else {
121+
test('from UI Runtime to UI Runtime', async () => {
122+
scheduleOnUI(() => {
123+
'worklet';
124+
try {
125+
const result = runOnRuntimeSyncWithId(UIRuntimeId, () => {
126+
'worklet';
127+
return 42;
128+
});
129+
scheduleOnRN(callbackPass, result);
130+
} catch (error) {
131+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
132+
}
133+
});
134+
135+
await waitForNotification(FAIL_NOTIFICATION);
136+
expect(reason).toBe(
137+
'[Worklets] runOnRuntimeSyncWithId cannot be called on Worklet Runtimes outside of the Bundle Mode.',
138+
);
139+
});
140+
141+
test('from UI Runtime to Worker Runtime', async () => {
142+
scheduleOnUI(() => {
143+
'worklet';
144+
try {
145+
const result = runOnRuntimeSyncWithId(workletRuntime1.runtimeId, () => {
146+
'worklet';
147+
return 42;
148+
});
149+
scheduleOnRN(callbackPass, result);
150+
} catch (error) {
151+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
152+
}
153+
});
154+
155+
await waitForNotification(FAIL_NOTIFICATION);
156+
expect(reason).toBe(
157+
'[Worklets] runOnRuntimeSyncWithId cannot be called on Worklet Runtimes outside of the Bundle Mode.',
158+
);
159+
});
160+
161+
test('from UI Runtime to non-existing Runtime', async () => {
162+
scheduleOnUI(() => {
163+
'worklet';
164+
try {
165+
const result = runOnRuntimeSyncWithId(9999, () => {
166+
'worklet';
167+
return 42;
168+
});
169+
scheduleOnRN(callbackPass, result);
170+
} catch (error) {
171+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
172+
}
173+
});
174+
175+
await waitForNotification(FAIL_NOTIFICATION);
176+
expect(reason).toBe(
177+
'[Worklets] runOnRuntimeSyncWithId cannot be called on Worklet Runtimes outside of the Bundle Mode.',
178+
);
179+
});
180+
}
181+
182+
if (globalThis._WORKLETS_BUNDLE_MODE_ENABLED) {
183+
test('from Worker Runtime to UI Runtime', async () => {
184+
scheduleOnRuntime(workletRuntime1, () => {
185+
'worklet';
186+
const result = runOnRuntimeSyncWithId(UIRuntimeId, () => {
187+
'worklet';
188+
return 42;
189+
});
190+
scheduleOnRN(callbackPass, result);
191+
});
192+
await waitForNotification(PASS_NOTIFICATION);
193+
expect(value).toBe(42);
194+
});
195+
test('from Worker Runtime to self', async () => {
196+
scheduleOnRuntime(workletRuntime1, () => {
197+
'worklet';
198+
const result = runOnRuntimeSyncWithId(workletRuntime1.runtimeId, () => {
199+
'worklet';
200+
return 42;
201+
});
202+
scheduleOnRN(callbackPass, result);
203+
});
204+
await waitForNotification(PASS_NOTIFICATION);
205+
expect(value).toBe(42);
206+
});
207+
test('from Worker Runtime to other Worker Runtime', async () => {
208+
scheduleOnRuntime(workletRuntime1, () => {
209+
'worklet';
210+
const result = runOnRuntimeSyncWithId(workletRuntime2.runtimeId, () => {
211+
'worklet';
212+
return 42;
213+
});
214+
scheduleOnRN(callbackPass, result);
215+
});
216+
await waitForNotification(PASS_NOTIFICATION);
217+
expect(value).toBe(42);
218+
});
219+
test('from Worker Runtime to non-existing Runtime', async () => {
220+
scheduleOnRuntime(workletRuntime1, () => {
221+
'worklet';
222+
try {
223+
const result = runOnRuntimeSyncWithId(9999, () => {
224+
'worklet';
225+
return 42;
226+
});
227+
scheduleOnRN(callbackPass, result);
228+
} catch (error) {
229+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
230+
}
231+
});
232+
233+
await waitForNotification(FAIL_NOTIFICATION);
234+
expect(reason).toBe('[Worklets] runOnRuntimeSyncWithId: no worklet runtime found for id 9999');
235+
});
236+
} else {
237+
test('from Worker Runtime to UI Runtime', async () => {
238+
scheduleOnRuntime(workletRuntime1, () => {
239+
'worklet';
240+
try {
241+
const result = runOnRuntimeSyncWithId(UIRuntimeId, () => {
242+
'worklet';
243+
return 42;
244+
});
245+
scheduleOnRN(callbackPass, result);
246+
} catch (error) {
247+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
248+
}
249+
});
250+
251+
await waitForNotification(FAIL_NOTIFICATION);
252+
expect(reason).toBe(
253+
'[Worklets] runOnRuntimeSyncWithId cannot be called on Worklet Runtimes outside of the Bundle Mode.',
254+
);
255+
});
256+
257+
test('from Worker Runtime to self', async () => {
258+
scheduleOnRuntime(workletRuntime1, () => {
259+
'worklet';
260+
try {
261+
const result = runOnRuntimeSyncWithId(workletRuntime1.runtimeId, () => {
262+
'worklet';
263+
return 42;
264+
});
265+
scheduleOnRN(callbackPass, result);
266+
} catch (error) {
267+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
268+
}
269+
});
270+
271+
await waitForNotification(FAIL_NOTIFICATION);
272+
expect(reason).toBe(
273+
'[Worklets] runOnRuntimeSyncWithId cannot be called on Worklet Runtimes outside of the Bundle Mode.',
274+
);
275+
});
276+
277+
test('from Worker Runtime to other Worker Runtime', async () => {
278+
scheduleOnRuntime(workletRuntime1, () => {
279+
'worklet';
280+
try {
281+
const result = runOnRuntimeSyncWithId(workletRuntime2.runtimeId, () => {
282+
'worklet';
283+
return 42;
284+
});
285+
scheduleOnRN(callbackPass, result);
286+
} catch (error) {
287+
scheduleOnRN(callbackFail, error instanceof Error ? error.message : String(error));
288+
}
289+
});
290+
291+
await waitForNotification(FAIL_NOTIFICATION);
292+
expect(reason).toBe(
293+
'[Worklets] runOnRuntimeSyncWithId cannot be called on Worklet Runtimes outside of the Bundle Mode.',
294+
);
295+
});
296+
}
297+
});

0 commit comments

Comments
 (0)