Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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 @@ -58,6 +58,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,56 @@
import {
createSynchronizable,
createWorkletRuntime,
runOnJS,
runOnRuntimeSync,
scheduleOnRuntime,
} from 'react-native-worklets';
import { describe, expect, test, notify, waitForNotification } from '../../ReJest/RuntimeTestsApi';
import { ComparisonMode } from '../../ReJest/types';

const NOTIFICATION_NAME = 'NOTIFICATION_NAME';

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);
});

test('keep the correct order of execution runOnRuntimeSync and scheduleOnRuntime', async () => {
// Arrange
const workletRuntime = createWorkletRuntime({ name: 'test' });
const synchronizable = createSynchronizable(0);

const onJSCallback = () => {
notify(NOTIFICATION_NAME);
};

// Act
scheduleOnRuntime(workletRuntime, () => {
'worklet';
// heavy computation
new Array(50_000_000).map((_v, i) => i ** 2);
synchronizable.setBlocking(100);
runOnJS(onJSCallback)();
});

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

// Assert
expect(result).toBe(100, ComparisonMode.NUMBER);
await waitForNotification(NOTIFICATION_NAME);
expect(synchronizable.getBlocking()).toBe(100, ComparisonMode.NUMBER);
});
});
81 changes: 81 additions & 0 deletions packages/docs-worklets/docs/threading/runOnRuntimeSync.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
sidebar_position: 13
---

# runOnRuntimeSync

`runOnRuntimeSync` lets you run a [workletized](/docs/fundamentals/glossary#to-workletize) function synchronously on a [Worker Runtime](/docs/fundamentals/glossary#worker-worklet-runtime---worker-runtime).

## Reference

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

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

const result = runOnRuntimeSync(workletRuntime, () => {
'worklet';
return 2+2;
});

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 [Worklet Runtime](/docs/fundamentals/glossary#worklet-runtime).

### args

Arguments to the function you want to execute on the [Worklet Runtime](/docs/fundamentals/glossary#worklet-runtime).

## Remarks

- The worklet cannot be run on the Worker Runtime from [UI
Runtime](/docs/fundamentals/glossary#ui-runtime) or another [Worker
Runtime](/docs/fundamentals/glossary#worker-worklet-runtime---worker-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 🚨
});
```
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 @@ -202,6 +202,7 @@ std::vector<jsi::PropNameID> JSIWorkletsModuleProxy::getPropertyNames(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "scheduleOnUI"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "executeOnUIRuntimeSync"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "runOnRuntimeSync"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createWorkletRuntime"));
propertyNames.emplace_back(
Expand Down Expand Up @@ -496,6 +497,19 @@ jsi::Value JSIWorkletsModuleProxy::get(
});
}

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 worklets::runOnRuntimeSync(rt, args[0], args[1]);
});
}

if (name == "createWorkletRuntime") {
return jsi::Function::createFromHostFunction(
rt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,12 @@ jsi::Value WorkletRuntime::executeSync(
auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>(
rt,
worklet,
"[Worklets] Only worklets can be executed synchronously on UI runtime.");
"[Worklets] Only worklets can be executed synchronously on Worklet (" +
name_ + ") Runtime.");
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
jsi::Runtime &uiRuntime = getJSIRuntime();
jsi::Runtime &workletRuntime = getJSIRuntime();
auto result = runGuarded(serializableWorklet);
auto serializableResult = extractSerializableOrThrow(uiRuntime, result);
auto serializableResult = extractSerializableOrThrow(workletRuntime, result);
lock.unlock();
return serializableResult->toJSValue(rt);
}
Expand Down Expand Up @@ -241,4 +242,11 @@ void scheduleOnRuntime(
workletRuntime->runAsyncGuarded(serializableWorklet);
}

jsi::Value runOnRuntimeSync(
jsi::Runtime &rt,
const jsi::Value &workletRuntimeValue,
const jsi::Value &serializableWorkletValue) {
auto workletRuntime = extractWorkletRuntime(rt, workletRuntimeValue);
return workletRuntime->executeSync(rt, serializableWorkletValue);
}
} // namespace worklets
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,8 @@ void scheduleOnRuntime(
const jsi::Value &workletRuntimeValue,
const jsi::Value &serializableWorkletValue);

jsi::Value runOnRuntimeSync(
jsi::Runtime &rt,
const jsi::Value &workletRuntimeValue,
const jsi::Value &serializableWorkletValue);
} // namespace worklets
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, runOnUISync } 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 @@ -135,6 +135,12 @@ class JSWorklets implements IWorkletsModule {
);
}

runOnRuntimeSync(): never {
throw new WorkletsError(
'`runOnRuntimeSync` is not available in JSWorklets.'
);
}

createWorkletRuntime(): never {
throw new WorkletsError(
'createWorkletRuntime is not available in JSWorklets.'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ 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 @@ -88,6 +88,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 @@ -20,6 +20,7 @@ export { isSynchronizable } from './isSynchronizable';
export { getRuntimeKind, RuntimeKind } from './runtimeKind';
export {
createWorkletRuntime,
runOnRuntimeSync,
runOnRuntime,
scheduleOnRuntime,
} from './runtimes';
Expand Down
50 changes: 50 additions & 0 deletions packages/react-native-worklets/src/runtimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,56 @@ export function scheduleOnRuntime<Args extends unknown[], ReturnValue>(
runOnRuntime(workletRuntime, worklet)(...args);
}

/**
* Lets you run a function synchronously on the [Worker
* Runtime](https://docs.swmansion.com/react-native-worklets/docs/fundamentals/glossary#worker-worklet-runtime---worker-runtime).
*
* - This function cannot be called from the [UI
* Runtime](https://docs.swmansion.com/react-native-worklets/docs/fundamentals/glossary#ui-runtime).
* or another [Worker
* Runtime](https://docs.swmansion.com/react-native-worklets/docs/fundamentals/glossary#worker-worklet-runtime---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__ && !SHOULD_BE_USE_WEB && !isWorkletFunction(worklet)) {
throw new WorkletsError(
'The function passed to `runOnRuntimeSync` is not a worklet.'
);
}

if (globalThis.__RUNTIME_KIND !== RuntimeKind.ReactNative) {
return WorkletsModule.runOnRuntimeSync(
workletRuntime,
makeShareableCloneOnUIRecursive(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why on worker runtime we want to call makeShareableCloneOnUIRecursive? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an old name, it was created before the functionality of creating custom Worklet Runtimes was added. This function should be called makeShareableCloneOnWorkletRuntimeRecursive, but as we discussed yesterday with @tjzel, we will not change it

'worklet';
const result = worklet(...args);
return createSerializable(result);
})
);
}

return WorkletsModule.runOnRuntimeSync(
workletRuntime,
createSerializable(() => {
'worklet';
const result = worklet(...args);
return makeShareableCloneOnUIRecursive(result);
})
);
}

/** Configuration object for creating a worklet runtime. */
export type WorkletRuntimeConfig = {
/** The name of the worklet runtime. */
Expand Down
Loading