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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The app exercises:
- Threaded-runtime list surfaces with FlashList and LegendList.
- Whole-screen threaded rendering for chat-style flows.
- Shared state across runtimes through `@react-native-runtimes/state`.
- Runtime prewarming, headless tasks, and a two-runtime architecture example.
- Runtime prewarming, scheduled runtime functions, and a two-runtime architecture example.

## Running

Expand Down
140 changes: 118 additions & 22 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions example/__tests__/cross-runtime.harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
//
// What we observed when the suite was enabled (Android, dev mode, harness on
// emulator):
// - The main runtime registers `ThreadedRuntimeFunctionRunner` /
// `ThreadedRuntimeHeadlessTaskRunner` as callable modules when
// - The main runtime registers `ThreadedRuntimeFunctionRunner` as a callable
// module when
// `@react-native-runtimes/core` is imported (top of ThreadedRuntime.tsx).
// - The worker runtime's JS bundle is loaded but the app's user-bundle code
// (`__r(0)` -> index.js -> .threaded-runtime/entry.js -> @react-native-runtimes/core)
Expand Down
26 changes: 25 additions & 1 deletion example/__tests__/runtime-function.harness.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'react-native-harness';
import { call, runtimeFunction } from '@react-native-runtimes/core';
import { call, runtimeFunction, schedule } from '@react-native-runtimes/core';

describe('runtimeFunction()', () => {
it('returns a callable that invokes the wrapped fn in-place', () => {
Expand All @@ -12,6 +12,11 @@ describe('runtimeFunction()', () => {
expect(typeof wrapped.runOn).toBe('function');
});

it('attaches a scheduleOn method to the returned function', () => {
const wrapped = runtimeFunction(() => {});
expect(typeof wrapped.scheduleOn).toBe('function');
});

it('does not attach __runtimeFunction metadata when no id is provided', () => {
const wrapped = runtimeFunction(() => 1);
expect(wrapped.__runtimeFunction).toBeUndefined();
Expand Down Expand Up @@ -41,6 +46,11 @@ describe('runtimeFunction.withId() / .named()', () => {
expect(typeof fn.runOn).toBe('function');
});

it('still exposes scheduleOn on the constructed function', () => {
const fn = runtimeFunction.withId('test/withId.scheduleOn', () => {});
expect(typeof fn.scheduleOn).toBe('function');
});

it('treats withId(fn) with empty id as still annotated', () => {
// Empty id is falsy in attachRuntimeFunction, so no metadata is attached.
const fn = runtimeFunction.withId('', () => 0);
Expand All @@ -61,3 +71,17 @@ describe('call(fn).on(runtime)', () => {
expect(typeof invoker).toBe('function');
});
});

describe('schedule(fn).on(runtime)', () => {
it('returns a builder with an .on(runtimeName) method', () => {
const fn = runtimeFunction(() => {});
const builder = schedule(fn);
expect(typeof builder.on).toBe('function');
});

it('.on(runtime) returns an invoker function', () => {
const fn = runtimeFunction(() => {});
const invoker = schedule(fn).on('any-runtime');
expect(typeof invoker).toBe('function');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class MainApplication : Application(), ReactApplication {
applicationContext,
"two-runtimes-business-runtime",
)
ThreadedRuntime.dispatchHeadlessTask(
ThreadedRuntime.schedule(
applicationContext,
"two-runtimes-business-runtime",
"twoRuntimes:startBusinessRuntime",
Expand Down
4 changes: 2 additions & 2 deletions example/ios/NativeComposeChat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
PODFILE_DIR = "/Users/riteshshukla/Desktop/development/opensource/react-native-runtimes/example/ios";
PODFILE_DIR = "/Users/szymon.chmal/Projects/react-native-runtimes/example/ios";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
Expand Down Expand Up @@ -468,7 +468,7 @@
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
PODFILE_DIR = "/Users/riteshshukla/Desktop/development/opensource/react-native-runtimes/example/ios";
PODFILE_DIR = "/Users/szymon.chmal/Projects/react-native-runtimes/example/ios";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
Expand Down
6 changes: 3 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2261,7 +2261,7 @@ SPEC CHECKSUMS:
Ease: 91720d5a28047201547bdcd0747db99a27e01fa7
FBLazyVector: 9266e314e3d76052e236e5a089812482de2aa92e
HarnessUI: 7f27d320d7f2ac3fde60ffff25f0baedb40aac4c
hermes-engine: 3c82959cae1a915edfe4922eb2e7a6093bafef22
hermes-engine: 9e9c9fb1815b9cd60bf21030fb500d9067dadcba
NativeComposeThreadedRuntime: 4b8350496d42f24047077b5b4204a7d1ab3050c5
NativeComposeThreadedZustand: f06b87ae6025c9d7b8ae10d3ea606b4b08854f75
NitroModules: d9e08ab81a7b5f6d16acbbc6ea6091e45ce34a9f
Expand All @@ -2273,7 +2273,7 @@ SPEC CHECKSUMS:
React: 01173c5d780d9bbfe022f6347d90de9a9917c980
React-callinvoker: 0e8cf2535617aefe9649d77ced059084a8ed371c
React-Core: 7c73a86183b3adbe000a5642dca4ecf3c7180562
React-Core-prebuilt: a73df132b7e1c1fb55447b7794150bcbeb0d4af2
React-Core-prebuilt: ab3d0e8e358d63d7719fb8170bc014ca1c34dda6
React-CoreModules: 2cdef98304cbd163282170161bd292714e35b24a
React-cxxreact: 375824e8216b8209538fdc3d5725b036779e3a86
React-debug: 73ce5e5d53291fab2b76a24e121595853cc56b4f
Expand Down Expand Up @@ -2337,7 +2337,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 43c8afa3c7a753e9ae311c5f05885175378ae682
ReactCodegen: a65ae5b76c474ca9091fc6f0daf77252e75d6e79
ReactCommon: d0213c6af9f4383453edb61862105a504ce2d0fd
ReactNativeDependencies: 0a6f0cf7e0238dea3f2933dbb2613590143e7296
ReactNativeDependencies: 6270afb21f2634b8f40643b427a797d4ba88a841
Yoga: 6876206cf24ad7b7625bbbc9c7fccb4c2ca8a174

PODFILE CHECKSUM: 5e017a3ef51cf1fa8f57fe3231df084e671c0783
Expand Down
31 changes: 11 additions & 20 deletions example/src/examples/twoRuntimesArchitecture.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
ThreadedRuntime,
registerThreadedHeadlessTask,
runtimeFunction,
schedule,
usingRuntime,
} from '@react-native-runtimes/core';
import { createSharedStore } from '@react-native-runtimes/state';
Expand Down Expand Up @@ -40,8 +40,8 @@ type TwoRuntimeBusinessTaskPayload = {
export const TWO_RUNTIMES_BUSINESS_RUNTIME_NAME =
'two-runtimes-business-runtime';

const TWO_RUNTIMES_BUSINESS_TASK = 'twoRuntimes:startBusinessRuntime';
const TWO_RUNTIMES_SYNC_TASK = 'twoRuntimes:syncNow';
export const TWO_RUNTIMES_START_BUSINESS_FUNCTION =
'twoRuntimes:startBusinessRuntime';

const initialBusinessStatus: TwoRuntimeBusinessStatus = {
bootedAt: null,
Expand Down Expand Up @@ -160,9 +160,9 @@ async function publishBusinessSnapshot(

let businessLoop: ReturnType<typeof setInterval> | null = null;

registerThreadedHeadlessTask<TwoRuntimeBusinessTaskPayload>(
TWO_RUNTIMES_BUSINESS_TASK,
async ({ payload }) => {
export const startTwoRuntimeBusinessLoop = runtimeFunction.named(
TWO_RUNTIMES_START_BUSINESS_FUNCTION,
async (payload: TwoRuntimeBusinessTaskPayload) => {
await twoRuntimeArchitectureStore.hydrate();

if (businessLoop) {
Expand All @@ -181,14 +181,6 @@ registerThreadedHeadlessTask<TwoRuntimeBusinessTaskPayload>(
},
);

registerThreadedHeadlessTask<TwoRuntimeBusinessTaskPayload>(
TWO_RUNTIMES_SYNC_TASK,
async ({ payload }) => {
await twoRuntimeArchitectureStore.hydrate();
await publishBusinessSnapshot(payload.command ?? 'manual sync', payload);
},
);

export const syncTwoRuntimeBusinessSnapshot = runtimeFunction(
async (payload: TwoRuntimeBusinessTaskPayload) => {
await twoRuntimeArchitectureStore.hydrate();
Expand Down Expand Up @@ -218,12 +210,11 @@ export async function startTwoRuntimeBusinessRuntime(startedBy: string) {
await ThreadedRuntime.prewarmBusinessRuntime(
TWO_RUNTIMES_BUSINESS_RUNTIME_NAME,
);
await ThreadedRuntime.runHeadlessTask(TWO_RUNTIMES_BUSINESS_TASK, {
runtimeName: TWO_RUNTIMES_BUSINESS_RUNTIME_NAME,
payload: {
enqueuedAt: Date.now(),
startedBy,
},
await schedule(startTwoRuntimeBusinessLoop).on(
TWO_RUNTIMES_BUSINESS_RUNTIME_NAME,
)({
enqueuedAt: Date.now(),
startedBy,
});
}

Expand Down
Loading