Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default function RuntimeTestsExample() {
require('./tests/runtimes/runOnUISync.test');
require('./tests/runtimes/scheduleOnRuntime.test');
require('./tests/runtimes/scheduleOnUI.test');
require('./tests/runtimes/runOnRuntimeSync.test');
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createWorkletRuntime, runOnRuntimeSync } from 'react-native-worklets';
import { describe, expect, test } from '../../ReJest/RuntimeTestsApi';
import { ComparisonMode } from '../../ReJest/types';

describe('runOnRuntimeSync', () => {
test('use runOnRuntimeSync to run a function on the Worker Runtime from RN Runtime', () => {
// Arrange
const workletRuntime = createWorkletRuntime({ name: 'test' });

// Act
const result = runOnRuntimeSync(workletRuntime, () => {
'worklet';
return 100;
});

// Assert
expect(result).toBe(100, ComparisonMode.NUMBER);
});
});
Original file line number Diff line number Diff line change
@@ -1,46 +1,16 @@
import { runOnUISync } from 'react-native-worklets';
import {
describe,
expect,
getRegisteredValue,
registerValue,
render,
test,
waitForNotification,
notify,
} from '../../ReJest/RuntimeTestsApi';
import { SharedValue, useSharedValue } from 'react-native-reanimated';
import { describe, expect, test } from '../../ReJest/RuntimeTestsApi';
import { ComparisonMode } from '../../ReJest/types';
import { View } from 'react-native';
import { useEffect } from 'react';

const SHARED_VALUE_REF = 'SHARED_VALUE_REF';
const NOTIFICATION_NAME = 'NOTIFICATION_NAME';

const TestComponent = () => {
const sharedValue = useSharedValue(0);
registerValue(SHARED_VALUE_REF, sharedValue as SharedValue<unknown>);

useEffect(() => {
const callback = (num: number) => {
'worklet';
sharedValue.value = num;
notify(NOTIFICATION_NAME);
};
runOnUISync(callback, 100);
}, [sharedValue]);

return <View />;
};

describe('runOnUISync', () => {
test('use runOnUISync to run a function on the UI runtime', async () => {
test('use runOnUISync to run a function on the UI Runtime from RN Runtime', () => {
// Arrange & Act
await render(<TestComponent />);
const result = runOnUISync(() => {
'worklet';
return 100;
});

// Assert
await waitForNotification(NOTIFICATION_NAME);
const sharedValueOnJS = await getRegisteredValue(SHARED_VALUE_REF);
expect(sharedValueOnJS.onJS).toBe(100, ComparisonMode.NUMBER);
expect(result).toBe(100, ComparisonMode.NUMBER);
});
});
79 changes: 79 additions & 0 deletions docs/docs-worklets/docs/threading/runOnRuntimeSync.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: runOnRuntimeSync
sidebar_position: 42 # Use alphabetical order
---

# runOnRuntimeSync <AvailableFrom version="0.8.0"/>

`runOnRuntimeSync` lets you run a [workletized](/docs/fundamentals/glossary#to-workletize) function synchronously on a [Worker Runtime](/docs/fundamentals/runtimeKinds#worker-runtime). It's blocking - meaning that it can preempt the runtime from a thread that's currently executing it.

## Reference

```javascript
import { runOnRuntimeSync, createWorkletRuntime } from 'react-native-worklets';

const workletRuntime = createWorkletRuntime({ name: 'background' });

const result = runOnRuntimeSync(workletRuntime, () => {
'worklet';
return 2 + 2;
}); // This will block the RN Runtime until the worklet is finished

console.log(result); // 4
```

<details>
<summary>Type definitions</summary>

```typescript
function runOnRuntimeSync<Args extends unknown[], ReturnValue>(
workletRuntime: WorkletRuntime,
worklet: (...args: Args) => ReturnValue,
...args: Args
): ReturnValue;
```

</details>

## Arguments

### workletRuntime

The worklet runtime to run the worklet on.

### worklet

A reference to a function you want to execute on the [Worker Runtime](/docs/fundamentals/runtimeKinds#worker-runtime).

### args

Arguments to the function you want to execute on the [Worker Runtime](/docs/fundamentals/runtimeKinds#worker-runtime).

## Remarks

- `runOnRuntimeSync` can only be called on the [RN Runtime](/docs/fundamentals/glossary#react-native-runtime) unless the [Bundle Mode](/docs/experimental/bundleMode) is enabled.

```javascript
import { createWorkletRuntime, runOnRuntimeSync } from 'react-native-worklets';

const workletRuntime = createWorkletRuntime({ name: 'background' });

runOnUI(() => {
runOnRuntimeSync(workletRuntime, (greeting: string) => {
console.log(`${greeting} from the background Worklet Runtime`);
}, 'Hello'); // This will throw an error outside of Bundle Mode 🚨
});
```

```javascript
import { createWorkletRuntime, scheduleOnRuntime } from 'react-native-worklets';

const workletRuntime = createWorkletRuntime({ name: 'background' });
const anotherWorkletRuntime = createWorkletRuntime({ name: 'anotherBackground' });

runOnRuntimeSync(anotherWorkletRuntime, () => {
runOnRuntimeSync(workletRuntime, (greeting: string) => {
console.log(`${greeting} from the background Worklet Runtime`);
}, 'Hello'); // This will throw an error outside of Bundle Mode 🚨
});
```
2 changes: 1 addition & 1 deletion docs/docs-worklets/docs/threading/scheduleOnRuntime.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ const anotherWorkletRuntime = createWorkletRuntime({ name: 'anotherBackground' }
scheduleOnRuntime(anotherWorkletRuntime, () => {
scheduleOnRuntime(workletRuntime, (greeting: string) => {
console.log(`${greeting} from the background Worklet Runtime`);
}, 'Hello'); // This will throw an error 🚨
}, 'Hello'); // This will throw an error outside of Bundle Mode 🚨
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ inline void scheduleOnUI(
});
}

inline jsi::Value executeOnUIRuntimeSync(
const std::weak_ptr<WorkletRuntime> &weakUIWorkletRuntime,
jsi::Runtime &rt,
const jsi::Value &worklet) {
inline jsi::Value
runOnUISync(const std::weak_ptr<WorkletRuntime> &weakUIWorkletRuntime, jsi::Runtime &rt, const jsi::Value &worklet) {
if (auto uiWorkletRuntime = weakUIWorkletRuntime.lock()) {
auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>(
rt, worklet, "[Worklets] Only worklets can be executed on UI runtime.");
Expand All @@ -67,6 +65,14 @@ inline jsi::Value executeOnUIRuntimeSync(
return jsi::Value::undefined();
}

jsi::Value
runOnRuntimeSync(jsi::Runtime &rt, const jsi::Value &workletRuntimeValue, const jsi::Value &serializableWorkletValue) {
auto workletRuntime = workletRuntimeValue.getObject(rt).getHostObject<WorkletRuntime>(rt);
auto worklet = extractSerializableOrThrow<SerializableWorklet>(
rt, serializableWorkletValue, "[Worklets] Only worklets can be executed on a worklet runtime.");
return workletRuntime->runSyncSerialized(worklet)->toJSValue(rt);
}

inline jsi::Value createWorkletRuntime(
jsi::Runtime &originRuntime,
const std::shared_ptr<RuntimeManager> &runtimeManager,
Expand Down Expand Up @@ -203,7 +209,8 @@ std::vector<jsi::PropNameID> JSIWorkletsModuleProxy::getPropertyNames(jsi::Runti
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "registerCustomSerializable"));

propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "scheduleOnUI"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "executeOnUIRuntimeSync"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "runOnUISync"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "runOnRuntimeSync"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createWorkletRuntime"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "scheduleOnRuntime"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "reportFatalErrorOnJS"));
Expand Down Expand Up @@ -385,14 +392,21 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
});
}

if (name == "executeOnUIRuntimeSync") {
if (name == "runOnUISync") {
return jsi::Function::createFromHostFunction(
rt,
propName,
1,
[uiWorkletRuntime = uiWorkletRuntime_](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return executeOnUIRuntimeSync(uiWorkletRuntime, rt, args[0]);
return runOnUISync(uiWorkletRuntime, rt, args[0]);
});
}

if (name == "runOnRuntimeSync") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi ::Value &thisValue, const jsi::Value *args, size_t count) {
return runOnRuntimeSync(rt, args[0], args[1]);
});
}

Expand Down
46 changes: 46 additions & 0 deletions packages/react-native-worklets/__typetests__/runOnRuntimeSync.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { createWorkletRuntime, runOnRuntimeSync } from '..';

function runOnRuntimeSyncTypeTests() {
const workletRuntime = createWorkletRuntime({ name: 'test' });
// Correct usage - correct usage
runOnRuntimeSync(
workletRuntime,
(num: number): number => {
return num + 1;
},
0
);

// @ts-expect-error - expected no args, but arg is provided
runOnRuntimeSync(workletRuntime, (): void => {}, 0);

// Wrong args type
runOnRuntimeSync(
workletRuntime,
(num: number): number => {
return num + 1;
},
// @ts-expect-error - wrong args type
'tets'
);

// Wrong return type
runOnRuntimeSync(
workletRuntime,
(num: number): string => {
// @ts-expect-error - wrong return type
return num + 1;
},
0
);

// @ts-expect-error - wrong return type
const result: string = runOnRuntimeSync(
workletRuntime,
(num: number): number => {
return num + 1;
},
0
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,8 @@ See https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting
return this.#workletsModuleProxy.scheduleOnUI(serializable);
}

executeOnUIRuntimeSync<TValue, TReturn>(
serializable: SerializableRef<TValue>
): TReturn {
return this.#workletsModuleProxy.executeOnUIRuntimeSync(serializable);
runOnUISync<TValue, TReturn>(worklet: SerializableRef<TValue>): TReturn {
return this.#workletsModuleProxy.runOnUISync(worklet);
}

createWorkletRuntime(
Expand Down Expand Up @@ -209,6 +207,13 @@ See https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting
);
}

runOnRuntimeSync<TValue, TReturn>(
workletRuntime: WorkletRuntime,
worklet: SerializableRef<TValue>
): TReturn {
return this.#workletsModuleProxy.runOnRuntimeSync(workletRuntime, worklet);
}

createSynchronizable<TValue>(value: TValue): SynchronizableRef<TValue> {
return this.#workletsModuleProxy.createSynchronizable(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ export interface WorkletsModuleProxy {

scheduleOnUI<TValue>(serializable: SerializableRef<TValue>): void;

executeOnUIRuntimeSync<TValue, TReturn>(
serializable: SerializableRef<TValue>
): TReturn;
runOnUISync<TValue, TReturn>(serializable: SerializableRef<TValue>): TReturn;

createWorkletRuntime(
name: string,
Expand All @@ -100,6 +98,11 @@ export interface WorkletsModuleProxy {
worklet: SerializableRef<TValue>
): void;

runOnRuntimeSync<TValue, TReturn>(
workletRuntime: WorkletRuntime,
worklet: SerializableRef<TValue>
): TReturn;

reportFatalErrorOnJS(
message: string,
stack: string,
Expand Down
1 change: 1 addition & 0 deletions packages/react-native-worklets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export { getRuntimeKind, RuntimeKind } from './runtimeKind';
export {
createWorkletRuntime,
runOnRuntime,
runOnRuntimeSync,
scheduleOnRuntime,
} from './runtimes';
export {
Expand Down
39 changes: 39 additions & 0 deletions packages/react-native-worklets/src/runtimes.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,42 @@ export function runOnRuntime<Args extends unknown[], ReturnValue>(
type WorkletRuntimeConfigInternal = WorkletRuntimeConfig & {
initializer?: WorkletFunction<[], void>;
};

/**
* Lets you run a function synchronously on a [Worker
* Runtime](https://docs.swmansion.com/react-native-worklets/docs/fundamentals/runtimeKinds#worker-runtime).
*
* - This function cannot be called from the [UI
* Runtime](https://docs.swmansion.com/react-native-worklets/docs/fundamentals/runtimeKinds#ui-runtime).
* or another [Worker
* Runtime](https://docs.swmansion.com/react-native-worklets/docs/fundamentals/runtimeKinds#worker-runtime),
* unless the [Bundle
* Mode](https://docs.swmansion.com/react-native-worklets/docs/experimental/bundleMode)
* is enabled.
*
* @param workletRuntime - The runtime to run the worklet on.
* @param worklet - The worklet to run.
* @param args - The arguments to pass to the worklet.
* @returns The return value of the worklet.
*/
export function runOnRuntimeSync<Args extends unknown[], ReturnValue>(
workletRuntime: WorkletRuntime,
worklet: (...args: Args) => ReturnValue,
...args: Args
): ReturnValue {
'worklet';
if (__DEV__ && !isWorkletFunction(worklet)) {
throw new WorkletsError(
'The function passed to `runOnRuntimeSync` is not a worklet.'
);
}

return WorkletsModule.runOnRuntimeSync(
workletRuntime,
createSerializable(() => {
'worklet';
const result = worklet(...args);
return makeShareableCloneOnUIRecursive(result);
})
);
}
10 changes: 10 additions & 0 deletions packages/react-native-worklets/src/runtimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,13 @@ export function scheduleOnRuntime<Args extends unknown[], ReturnValue>(
export function scheduleOnRuntime(): never {
throw new WorkletsError('`scheduleOnRuntime` is not supported on web.');
}

export function runOnRuntimeSync<Args extends unknown[], ReturnValue>(
workletRuntime: WorkletRuntime,
worklet: (...args: Args) => ReturnValue,
...args: Args
): ReturnValue;

export function runOnRuntimeSync(): never {
throw new WorkletsError('`runOnRuntimeSync` is not supported on web.');
}
Loading
Loading