@depup/react-native-worklets 0.11.4-depup.0 → 0.12.2-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/Common/cpp/worklets/AnimationFrameQueue/AnimationFrameBatchinator.cpp +6 -3
  2. package/Common/cpp/worklets/Compat/StableApi.cpp +4 -0
  3. package/Common/cpp/worklets/Compat/StableApi.h +3 -1
  4. package/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +26 -43
  5. package/Common/cpp/worklets/RunLoop/EventLoop.cpp +16 -7
  6. package/Common/cpp/worklets/RunLoop/EventLoop.h +4 -2
  7. package/Common/cpp/worklets/SharedItems/MemoryManager.cpp +2 -2
  8. package/Common/cpp/worklets/SharedItems/Serializable.cpp +17 -1
  9. package/Common/cpp/worklets/SharedItems/Serializable.h +14 -16
  10. package/Common/cpp/worklets/Tools/UIScheduler.cpp +10 -0
  11. package/Common/cpp/worklets/Tools/UIScheduler.h +3 -0
  12. package/Common/cpp/worklets/Tools/WorkletsJSIUtils.h +23 -0
  13. package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp +7 -4
  14. package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.h +2 -1
  15. package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +60 -88
  16. package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +335 -122
  17. package/README.md +7 -7
  18. package/android/build.gradle.kts +20 -6
  19. package/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp +10 -8
  20. package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.cpp +2 -11
  21. package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.h +1 -4
  22. package/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.kt +3 -0
  23. package/android/src/main/java/com/swmansion/worklets/ScriptBufferWrapper.kt +87 -43
  24. package/android/src/main/java/com/swmansion/worklets/runloop/AnimationFrameQueue.kt +37 -5
  25. package/android/src/networking/com/swmansion/worklets/WorkletsModule.kt +2 -3
  26. package/android/src/no-networking/com/swmansion/worklets/WorkletsModule.kt +2 -3
  27. package/apple/worklets/apple/IOSUIScheduler.h +3 -0
  28. package/apple/worklets/apple/IOSUIScheduler.mm +6 -1
  29. package/apple/worklets/apple/ScriptLoader.h +11 -0
  30. package/apple/worklets/apple/ScriptLoader.mm +124 -0
  31. package/apple/worklets/apple/WorkletsModule.h +1 -16
  32. package/apple/worklets/apple/WorkletsModule.mm +4 -16
  33. package/changes.json +6 -6
  34. package/compatibility.json +4 -1
  35. package/lib/module/WorkletsModule/NativeWorklets.native.js +2 -2
  36. package/lib/module/WorkletsModule/NativeWorklets.native.js.map +1 -1
  37. package/lib/module/debug/jsVersion.js +1 -1
  38. package/lib/module/memory/serializable.native.js +61 -23
  39. package/lib/module/memory/serializable.native.js.map +1 -1
  40. package/lib/module/memory/synchronizableUnpacker.native.js +16 -8
  41. package/lib/module/memory/synchronizableUnpacker.native.js.map +1 -1
  42. package/lib/module/mock.js +58 -17
  43. package/lib/module/mock.js.map +1 -1
  44. package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js +1 -2
  45. package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js.map +1 -1
  46. package/lib/module/runLoop/workletRuntime/taskQueue.js +0 -1
  47. package/lib/module/runLoop/workletRuntime/taskQueue.js.map +1 -1
  48. package/lib/module/runtimes.native.js +7 -8
  49. package/lib/module/runtimes.native.js.map +1 -1
  50. package/lib/module/threads.js +35 -24
  51. package/lib/module/threads.js.map +1 -1
  52. package/lib/typescript/WorkletsModule/NativeWorklets.native.d.ts.map +1 -1
  53. package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts +1 -1
  54. package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts.map +1 -1
  55. package/lib/typescript/debug/jsVersion.d.ts +1 -1
  56. package/lib/typescript/memory/serializable.native.d.ts.map +1 -1
  57. package/lib/typescript/memory/synchronizableUnpacker.native.d.ts.map +1 -1
  58. package/lib/typescript/runLoop/uiRuntime/requestAnimationFrame.d.ts.map +1 -1
  59. package/lib/typescript/runLoop/workletRuntime/taskQueue.d.ts.map +1 -1
  60. package/lib/typescript/runtimes.native.d.ts.map +1 -1
  61. package/lib/typescript/specs/NativeWorkletsModule.d.ts +1 -1
  62. package/lib/typescript/threads.d.ts.map +1 -1
  63. package/lib/typescript/types.d.ts +38 -8
  64. package/lib/typescript/types.d.ts.map +1 -1
  65. package/package.json +24 -25
  66. package/plugin/index.js +1625 -1572
  67. package/src/WorkletsModule/NativeWorklets.native.ts +4 -2
  68. package/src/WorkletsModule/workletsModuleProxy.ts +2 -1
  69. package/src/debug/jsVersion.ts +1 -1
  70. package/src/memory/serializable.native.ts +108 -38
  71. package/src/memory/synchronizableUnpacker.native.ts +16 -10
  72. package/src/mock.ts +85 -23
  73. package/src/runLoop/uiRuntime/requestAnimationFrame.ts +1 -2
  74. package/src/runLoop/workletRuntime/taskQueue.ts +0 -1
  75. package/src/runtimes.native.ts +12 -8
  76. package/src/threads.ts +38 -26
  77. package/src/types.ts +42 -8
  78. package/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp +0 -95
  79. package/Common/cpp/worklets/Registries/EventHandlerRegistry.h +0 -44
  80. package/Common/cpp/worklets/Tools/WorkletEventHandler.cpp +0 -34
  81. package/Common/cpp/worklets/Tools/WorkletEventHandler.h +0 -44
  82. package/jest/pluginTestUtils.ts +0 -16
@@ -10,10 +10,13 @@
10
10
  #include <worklets/Tools/JSLogger.h>
11
11
  #include <worklets/Tools/JSScheduler.h>
12
12
  #include <worklets/Tools/ScriptBuffer.h>
13
+ #include <worklets/Tools/WorkletsJSIUtils.h>
13
14
  #include <worklets/WorkletRuntime/RuntimeBindings.h>
14
15
  #include <worklets/WorkletRuntime/RuntimeData.h>
15
16
 
17
+ #include <cstdint>
16
18
  #include <memory>
19
+ #include <mutex>
17
20
  #include <optional>
18
21
  #include <string>
19
22
  #include <type_traits>
@@ -26,13 +29,16 @@ using namespace facebook;
26
29
  using namespace react;
27
30
 
28
31
  template <typename TCallable>
29
- concept ImplicitlySerializableCallable = std::is_assignable_v<const jsi::Function &, TCallable> ||
30
- std::is_assignable_v<const std::shared_ptr<SerializableWorklet> &, TCallable>;
32
+ concept RuntimeCallable = std::is_same_v<std::remove_cvref_t<TCallable>, jsi::Function> ||
33
+ std::is_same_v<std::remove_cvref_t<TCallable>, std::shared_ptr<SerializableWorklet>>;
31
34
 
32
- template <typename TCallable>
33
- concept RuntimeCallable = requires(TCallable &&callable, jsi::Runtime &rt) {
34
- { callable(rt) };
35
- } || ImplicitlySerializableCallable<TCallable>;
35
+ template <typename TJob>
36
+ concept RuntimeJob = !RuntimeCallable<TJob> && requires(TJob &&job, jsi::Runtime &rt) {
37
+ { job(rt) };
38
+ };
39
+
40
+ template <typename TResult>
41
+ concept SyncCallResult = std::is_same_v<TResult, jsi::Value> || std::is_same_v<TResult, std::shared_ptr<Serializable>>;
36
42
 
37
43
  /**
38
44
  * Forward declaration to avoid circular dependencies.
@@ -41,105 +47,174 @@ class JSIWorkletsModuleProxy;
41
47
 
42
48
  class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_this<WorkletRuntime> {
43
49
  public:
44
- void schedule(jsi::Function &&function) const;
45
- void schedule(std::shared_ptr<SerializableWorklet> worklet) const;
46
- #ifndef NDEBUG
47
- void schedule(std::shared_ptr<SerializableWorklet> worklet, std::optional<std::string> scheduleStack) const;
48
- #endif // NDEBUG
49
- void schedule(std::function<void()> job) const;
50
+ /**
51
+ * Schedules a std::function to run asynchronously.
52
+ *
53
+ * Runs a single microtask checkpoint on completion.
54
+ */
50
55
  void schedule(std::function<void(jsi::Runtime &)> job) const;
51
56
 
52
- /* #region runSync */
57
+ /**
58
+ * Schedules a jsi::Function to run asynchronously.
59
+ *
60
+ * The jsi::Function has to originate from this runtime, otherwise it will
61
+ crash.
62
+ *
63
+ * Runs a single microtask checkpoint on completion.
64
+ */
65
+ void schedule(jsi::Function &&function) const;
53
66
 
54
- template <RuntimeCallable TCallable, typename... Args>
55
- std::invoke_result_t<TCallable, Args...> runSync(TCallable &&callable, Args &&...args) const;
56
- template <typename... Args>
57
- jsi::Value runSync(const jsi::Function &function, Args &&...args) const {
58
- #ifndef NDEBUG
59
- return callGuarded(function, std::nullopt, std::forward<Args>(args)...);
60
- #else
61
- return function.call(*runtime_, args...);
62
- #endif // NDEBUG
63
- }
64
- template <typename... Args>
65
- jsi::Value runSync(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args) const {
67
+ /**
68
+ * Schedules a serialized worklet to run asynchronously.
69
+ *
70
+ * Runs a single microtask checkpoint on completion.
71
+ */
72
+ void schedule(std::shared_ptr<SerializableWorklet> worklet) const;
73
+
74
+ /**
75
+ * Schedules a batch of serialized worklets to run asynchronously.
76
+ *
77
+ * Runs a single microtask checkpoint on completion of the batch.
78
+ */
79
+ void schedule(std::vector<std::shared_ptr<SerializableWorklet>> worklets) const;
66
80
  #ifndef NDEBUG
67
- return runSyncWithStack(worklet, std::nullopt, std::forward<Args>(args)...);
68
- #else
69
- jsi::Runtime &rt = *runtime_;
70
- auto function = worklet->toJSValue(rt).asObject(rt).asFunction(rt);
71
- return function.call(rt, args...);
81
+ /**
82
+ * Schedules a serialized worklet to run asynchronously on the worklet runtime,
83
+ * remembering the call site that scheduled it for error reporting.
84
+ *
85
+ * Runs a single microtask checkpoint on completion.
86
+ */
87
+ void scheduleWithStack(std::shared_ptr<SerializableWorklet> worklet, std::optional<std::string> scheduleStack) const;
88
+
89
+ /**
90
+ * Schedules a batch of serialized worklets to run asynchronously on the
91
+ * worklet runtime, remembering their scheduling call sites for error
92
+ * reporting.
93
+ *
94
+ * Runs a single microtask checkpoint on completion of the batch.
95
+ */
96
+ void scheduleWithStack(
97
+ std::vector<std::shared_ptr<SerializableWorklet>> worklets,
98
+ std::vector<std::optional<std::string>> scheduleStacks) const;
72
99
  #endif // NDEBUG
100
+
101
+ /**
102
+ * Runs a RuntimeJob synchronously and returns its result.
103
+ *
104
+ * Does not run a microtask checkpoint.
105
+ */
106
+ template <RuntimeJob TJob>
107
+ auto runSync(TJob &&job) const -> std::invoke_result_t<TJob, jsi::Runtime &> {
108
+ auto lock = acquireRuntimeLock();
109
+ return runSyncImpl(std::forward<TJob>(job));
73
110
  }
74
- #ifndef NDEBUG
75
- template <typename... Args>
76
- jsi::Value runSyncWithStack(
77
- const std::shared_ptr<SerializableWorklet> &worklet,
78
- const std::optional<std::string> &scheduleStack,
79
- Args &&...args) const {
80
- jsi::Runtime &rt = *runtime_;
81
- auto function = worklet->toJSValue(rt).asObject(rt).asFunction(rt);
82
- return callGuarded(function, scheduleStack, std::forward<Args>(args)...);
83
- }
84
- #endif // NDEBUG
85
- template <RuntimeCallable TCallable>
86
- std::invoke_result_t<TCallable, jsi::Runtime &> runSync(TCallable &&job) const {
87
- jsi::Runtime &rt = getJSIRuntime();
88
- auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
89
- return job(rt);
111
+
112
+ /**
113
+ * Runs a jsi::Function or SerializableWorklet synchronously and returns a
114
+ * jsi::Value result.
115
+ *
116
+ * The returned jsi::Value originates from this runtime and cannot be used on
117
+ * another runtime unless it is a primitive value. Use runSyncSerialized to
118
+ * transfer the result to another runtime.
119
+ *
120
+ * Does not run a microtask checkpoint.
121
+ */
122
+ template <RuntimeCallable TCallable, typename... TArgs>
123
+ auto runSync(const TCallable &callable, TArgs &&...args) const -> jsi::Value {
124
+ auto lock = acquireRuntimeLock();
125
+ return runSyncImpl(callable, std::forward<TArgs>(args)...);
90
126
  }
91
127
 
92
- /* #endregion */
128
+ /**
129
+ * Runs a jsi::Function or SerializableWorklet synchronously and returns its
130
+ * serialized result.
131
+ *
132
+ * Does not run a microtask checkpoint.
133
+ */
134
+ template <RuntimeCallable TCallable, typename... TArgs>
135
+ auto runSyncSerialized(const TCallable &callable, TArgs &&...args) const -> std::shared_ptr<Serializable> {
136
+ auto lock = acquireRuntimeLock();
137
+ return runSyncImpl<MicrotaskCheckpoint::Skip, std::shared_ptr<Serializable>>(
138
+ callable, std::forward<TArgs>(args)...);
139
+ }
93
140
 
94
- void callMicrotasks() const;
141
+ /**
142
+ * Runs a jsi::Function or SerializableWorklet synchronously and returns its
143
+ * serialized result.
144
+ *
145
+ * Runs a single microtask checkpoint on completion.
146
+ */
147
+ template <RuntimeJob TJob>
148
+ auto runSyncAndDrainMicrotasks(TJob &&job) const -> std::invoke_result_t<TJob, jsi::Runtime &> {
149
+ auto lock = acquireRuntimeLock();
150
+ return runSyncImpl<MicrotaskCheckpoint::Run>(std::forward<TJob>(job));
151
+ }
95
152
 
96
- /* #region runSyncSerialized */
153
+ /**
154
+ * Runs a jsi::Function or SerializableWorklet synchronously and returns a
155
+ * jsi::Value result.
156
+ *
157
+ * The returned jsi::Value originates from this runtime and cannot be used on
158
+ * another runtime unless it is a primitive value. Use
159
+ * runSyncAndDrainMicrotasksSerialized to transfer the result to another
160
+ * runtime.
161
+ *
162
+ * Runs a single microtask checkpoint on completion.
163
+ */
164
+ template <RuntimeCallable TCallable, typename... TArgs>
165
+ auto runSyncAndDrainMicrotasks(const TCallable &callable, TArgs &&...args) const -> jsi::Value {
166
+ auto lock = acquireRuntimeLock();
167
+ return runSyncImpl<MicrotaskCheckpoint::Run>(callable, std::forward<TArgs>(args)...);
168
+ }
97
169
 
98
- template <ImplicitlySerializableCallable TCallable, typename... Args>
99
- std::shared_ptr<Serializable> runSyncSerialized(TCallable &&callable, Args &&...args) const;
100
- template <typename... Args>
101
- std::shared_ptr<Serializable> runSyncSerialized(const jsi::Function &function, Args &&...args) const {
102
- jsi::Runtime &rt = getJSIRuntime();
103
- auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
104
- auto result = runSync(function, std::forward<Args>(args)...);
105
- auto serializableResult = extractSerializableOrThrow(
106
- rt,
107
- result,
108
- "[Worklets] Function passed to `runSyncSerialized`"
109
- "must return a value serialized with `createSerializable`.");
110
- return serializableResult;
170
+ /**
171
+ * Runs a jsi::Function or SerializableWorklet synchronously and returns its
172
+ * serialized result.
173
+ *
174
+ * Runs a single microtask checkpoint on completion.
175
+ */
176
+ template <RuntimeCallable TCallable, typename... TArgs>
177
+ auto runSyncAndDrainMicrotasksSerialized(const TCallable &callable, TArgs &&...args) const
178
+ -> std::shared_ptr<Serializable> {
179
+ auto lock = acquireRuntimeLock();
180
+ return runSyncImpl<MicrotaskCheckpoint::Run, std::shared_ptr<Serializable>>(callable, std::forward<TArgs>(args)...);
111
181
  }
182
+
112
183
  #ifndef NDEBUG
113
- template <typename... Args>
114
- std::shared_ptr<Serializable> runSyncSerializedWithStack(
115
- const std::shared_ptr<SerializableWorklet> &worklet,
184
+ /**
185
+ * Synchronously invokes a jsi::Function or SerializableWorklet on the worklet
186
+ * runtime and returns TResult, remembering the call site that scheduled it
187
+ * for error reporting.
188
+ *
189
+ * TResult must be either jsi::Value or std::shared_ptr<Serializable>.
190
+ *
191
+ * Does not run a microtask checkpoint.
192
+ */
193
+ template <SyncCallResult TResult, RuntimeCallable TCallable, typename... TArgs>
194
+ auto runSyncWithStack(const TCallable &callable, const std::optional<std::string> &scheduleStack, TArgs &&...args)
195
+ const -> TResult {
196
+ auto lock = acquireRuntimeLock();
197
+ return runSyncImpl<MicrotaskCheckpoint::Skip, TResult, ScheduleStack::Requested>(
198
+ callable, scheduleStack, std::forward<TArgs>(args)...);
199
+ }
200
+
201
+ /**
202
+ * Synchronously invokes a jsi::Function or SerializableWorklet on the worklet
203
+ * runtime, runs any microtasks it queued, and returns TResult, remembering the
204
+ * call site that scheduled it for error reporting.
205
+ *
206
+ * TResult must be either jsi::Value or std::shared_ptr<Serializable>.
207
+ */
208
+ template <SyncCallResult TResult, RuntimeCallable TCallable, typename... TArgs>
209
+ auto runSyncWithStackAndDrainMicrotasks(
210
+ const TCallable &callable,
116
211
  const std::optional<std::string> &scheduleStack,
117
- Args &&...args) const {
118
- jsi::Runtime &rt = getJSIRuntime();
119
- auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
120
- auto result = runSyncWithStack(worklet, scheduleStack, std::forward<Args>(args)...);
121
- auto serializableResult = extractSerializableOrThrow(
122
- rt,
123
- result,
124
- "[Worklets] Worklet passed to `runSyncSerialized`"
125
- "must return a value serialized with `createSerializable`.");
126
- return serializableResult;
212
+ TArgs &&...args) const -> TResult {
213
+ auto lock = acquireRuntimeLock();
214
+ return runSyncImpl<MicrotaskCheckpoint::Run, TResult, ScheduleStack::Requested>(
215
+ callable, scheduleStack, std::forward<TArgs>(args)...);
127
216
  }
128
217
  #endif // NDEBUG
129
- template <typename... Args>
130
- std::shared_ptr<Serializable> runSyncSerialized(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args)
131
- const {
132
- jsi::Runtime &rt = getJSIRuntime();
133
- auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
134
- auto result = runSync(worklet, std::forward<Args>(args)...);
135
- auto serializableResult = extractSerializableOrThrow(
136
- rt,
137
- result,
138
- "[Worklets] Worklet passed to `runSyncSerialized`"
139
- "must return a value serialized with `createSerializable`.");
140
- return serializableResult;
141
- }
142
- /* #endregion */
143
218
 
144
219
  jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override;
145
220
 
@@ -161,63 +236,192 @@ class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_th
161
236
  return name_;
162
237
  }
163
238
 
239
+ [[nodiscard]] bool isLockingEnabled() const noexcept {
240
+ return enableLocking_;
241
+ }
242
+
164
243
  explicit WorkletRuntime(
165
244
  RuntimeData::RuntimeId runtimeId,
166
245
  RuntimeData::RuntimeKind runtimeKind,
167
246
  const std::string &name,
168
247
  const std::shared_ptr<AsyncQueue> &queue = nullptr,
169
- bool enableEventLoop = true);
248
+ bool enableEventLoop = true,
249
+ bool enableLocking = true);
170
250
 
171
251
  void init(const std::shared_ptr<JSIWorkletsModuleProxy> &jsiWorkletsModuleProxy);
172
252
 
173
- /* #region deprecated */
253
+ /**
254
+ * Retrieves a weak reference to the WorkletRuntime associated with the
255
+ * provided jsi::Runtime.
256
+ *
257
+ * Throws when invoked with a non-worklet runtime.
258
+ */
259
+ static std::weak_ptr<WorkletRuntime> getWeakRuntimeFromJSIRuntime(jsi::Runtime &rt);
174
260
 
175
- /** @deprecated Use `runSync` instead. */
176
- template <typename... Args>
177
- jsi::Value runGuarded(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args) const {
178
- return runSync(worklet, std::forward<Args>(args)...);
261
+ /**
262
+ * Runs the worklet runtime's pending microtasks.
263
+ *
264
+ * The runtime lock is held until the microtask checkpoint completes.
265
+ */
266
+ void drainMicrotasks() const {
267
+ auto lock = acquireRuntimeLock();
268
+ drainMicrotasksImpl();
179
269
  }
180
270
 
181
- /** @deprecated Use `schedule` instead. */
182
- void runAsyncGuarded(const std::shared_ptr<SerializableWorklet> &worklet);
271
+ private:
272
+ enum class MicrotaskCheckpoint : std::uint8_t {
273
+ Skip,
274
+ Run,
275
+ };
183
276
 
184
- /** @deprecated Use `runSyncSerialized` and extract to `jsi::Value` with
185
- * `extractSerializableOrThrow` instead. */
186
- jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const;
187
- /** @deprecated Use `runSync` instead. */
188
- jsi::Value executeSync(std::function<jsi::Value(jsi::Runtime &)> &&job) const;
189
- /** @deprecated Use `runSync` instead. */
190
- jsi::Value executeSync(const std::function<jsi::Value(jsi::Runtime &)> &job) const;
277
+ enum class ScheduleStack : std::uint8_t {
278
+ NotRequested,
279
+ #ifndef NDEBUG
280
+ Requested,
281
+ #endif // NDEBUG
282
+ };
191
283
 
192
- /* #endregion */
284
+ struct NoScheduleStack {};
285
+
286
+ #ifndef NDEBUG
287
+ struct RequestedScheduleStack {
288
+ const std::optional<std::string> &value;
289
+ };
290
+ #endif // NDEBUG
291
+
292
+ using ScheduledJob = std::function<void(const WorkletRuntime &)>;
193
293
 
194
294
  /**
195
- * Retrieves a weak reference to the WorkletRuntime associated with the
196
- * provided jsi::Runtime.
197
- *
198
- * Throws when invoked with a non-worklet runtime.
295
+ * Queues a job and acquires the runtime lock immediately before invoking it.
199
296
  *
200
- * Available only on React Native 0.81 and higher.
297
+ * Scheduled jobs must use the unlocked implementation methods.
201
298
  */
202
- static std::weak_ptr<WorkletRuntime> getWeakRuntimeFromJSIRuntime(jsi::Runtime &rt);
299
+ void scheduleImpl(ScheduledJob job) const;
203
300
 
204
- private:
301
+ /**
302
+ * Assumes the caller acquired the runtime lock.
303
+ */
304
+ template <
305
+ MicrotaskCheckpoint TCheckpoint = MicrotaskCheckpoint::Skip,
306
+ SyncCallResult TResult = jsi::Value,
307
+ ScheduleStack TScheduleStack = ScheduleStack::NotRequested,
308
+ typename TCallable,
309
+ typename... TArgs>
310
+ requires RuntimeJob<TCallable> || RuntimeCallable<TCallable>
311
+ auto runSyncImpl(TCallable &&callable, TArgs &&...args) const -> decltype(auto) {
312
+ jsi::Runtime &rt = getJSIRuntime();
313
+ if constexpr (std::is_same_v<std::remove_cvref_t<TCallable>, std::shared_ptr<SerializableWorklet>>) {
314
+ auto function = callable->toJSValue(rt).getObject(rt).getFunction(rt);
315
+ return runSyncImpl<TCheckpoint, TResult, TScheduleStack>(function, std::forward<TArgs>(args)...);
316
+ } else {
317
+ auto stackPolicyInvocation = [&]<typename TScheduleStackContext, typename... TCallArgs>(
318
+ TScheduleStackContext &&scheduleStackContext,
319
+ TCallArgs &&...callArgs) -> decltype(auto) {
320
+ auto microtaskPolicyInvocation = [&]() -> decltype(auto) {
321
+ if constexpr (RuntimeJob<TCallable>) {
322
+ return std::forward<TCallable>(callable)(rt);
323
+ } else {
324
+ auto result = this->invoke(
325
+ rt,
326
+ callable,
327
+ std::forward<TScheduleStackContext>(scheduleStackContext),
328
+ std::forward<TCallArgs>(callArgs)...);
329
+ if constexpr (std::is_same_v<TResult, std::shared_ptr<Serializable>>) {
330
+ return extractSerializableOrThrow(rt, result);
331
+ } else {
332
+ return result;
333
+ }
334
+ }
335
+ };
336
+
337
+ return invokeWithMicrotaskCheckpointPolicyImpl<TCheckpoint>(std::move(microtaskPolicyInvocation));
338
+ };
339
+
340
+ return invokeWithStackPolicyImpl<TScheduleStack, TCallable>(
341
+ std::move(stackPolicyInvocation), std::forward<TArgs>(args)...);
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Assumes the caller acquired the runtime lock.
347
+ */
348
+ template <ScheduleStack TScheduleStack, typename TCallable, typename TInvoker, typename... TArgs>
349
+ static auto invokeWithStackPolicyImpl(TInvoker &&invoke, TArgs &&...args) -> decltype(auto) {
350
+ #ifndef NDEBUG
351
+ if constexpr (TScheduleStack == ScheduleStack::Requested) {
352
+ static_assert(RuntimeCallable<TCallable>, "[Worklets] Scheduling stacks can be used only with worklet calls.");
353
+ static_assert(sizeof...(TArgs) > 0, "[Worklets] A scheduling stack argument is required.");
354
+ return [&]<typename TScheduleStackArg, typename... TCallArgs>(
355
+ TScheduleStackArg &&scheduleStack, TCallArgs &&...callArgs) -> decltype(auto) {
356
+ static_assert(
357
+ std::is_same_v<std::remove_cvref_t<TScheduleStackArg>, std::optional<std::string>>,
358
+ "[Worklets] The first argument must be an optional scheduling stack.");
359
+ return std::forward<TInvoker>(invoke)(
360
+ RequestedScheduleStack{std::forward<TScheduleStackArg>(scheduleStack)},
361
+ std::forward<TCallArgs>(callArgs)...);
362
+ }(std::forward<TArgs>(args)...);
363
+ } else
364
+ #endif // NDEBUG
365
+ {
366
+ return std::forward<TInvoker>(invoke)(NoScheduleStack{}, std::forward<TArgs>(args)...);
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Assumes the caller acquired the runtime lock.
372
+ */
373
+ template <MicrotaskCheckpoint TCheckpoint, typename TInvoker>
374
+ auto invokeWithMicrotaskCheckpointPolicyImpl(TInvoker &&invoke) const -> std::invoke_result_t<TInvoker> {
375
+ using Result = std::invoke_result_t<TInvoker>;
376
+ const auto checkpoint = [&]() {
377
+ if constexpr (TCheckpoint == MicrotaskCheckpoint::Run) {
378
+ drainMicrotasksImpl();
379
+ }
380
+ };
381
+ if constexpr (std::is_void_v<Result>) {
382
+ std::forward<TInvoker>(invoke)();
383
+ checkpoint();
384
+ } else {
385
+ Result result = std::forward<TInvoker>(invoke)();
386
+ checkpoint();
387
+ return std::forward<Result>(result);
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Assumes the caller acquired the runtime lock.
393
+ */
394
+ void drainMicrotasksImpl() const {
395
+ if (microtaskQueueEnabled_) {
396
+ jsi_utils::drainMicrotasks(getJSIRuntime());
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Invokes the provided function, reporting any exception with the scheduling
402
+ * stack carried by the provided stack context in debug builds.
403
+ */
404
+ template <typename TScheduleStackContext, typename... TArgs>
405
+ jsi::Value invoke(
406
+ jsi::Runtime &rt,
407
+ const jsi::Function &function,
408
+ const TScheduleStackContext &scheduleStackContext,
409
+ TArgs &&...args) const {
205
410
  #ifndef NDEBUG
206
- // Wraps the provided function in a try/catch so an exception thrown on the
207
- // worklet runtime can be reported on the RN Runtime LogBox with a
208
- // stack pointing back to the JS call site that scheduled the worklet.
209
- template <typename... Args>
210
- jsi::Value callGuarded(const jsi::Function &function, const std::optional<std::string> &scheduleStack, Args &&...args)
211
- const {
212
- auto &rt = *runtime_;
213
411
  try {
214
- return function.call(rt, args...);
412
+ return function.call(rt, std::forward<TArgs>(args)...);
215
413
  } catch (jsi::JSError &e) {
216
- JSLogger::handleJSError(jsScheduler_, rt, name_, e, scheduleStack);
414
+ if constexpr (std::is_same_v<TScheduleStackContext, RequestedScheduleStack>) {
415
+ JSLogger::handleJSError(jsScheduler_, rt, name_, e, scheduleStackContext.value);
416
+ } else {
417
+ JSLogger::handleJSError(jsScheduler_, rt, name_, e, std::nullopt);
418
+ }
217
419
  return jsi::Value::undefined();
218
420
  }
219
- }
421
+ #else
422
+ return function.call(rt, std::forward<TArgs>(args)...);
220
423
  #endif // NDEBUG
424
+ }
221
425
 
222
426
  void bundleModeInit(
223
427
  const std::shared_ptr<JSScheduler> &jsScheduler,
@@ -227,8 +431,17 @@ class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_th
227
431
 
228
432
  void legacyModeInit(const std::shared_ptr<UnpackerLoader> &unpackerLoader);
229
433
 
434
+ [[nodiscard]] std::unique_lock<std::recursive_mutex> acquireRuntimeLock() const {
435
+ if (enableLocking_) {
436
+ return std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
437
+ }
438
+ return {};
439
+ }
440
+
230
441
  const RuntimeData::RuntimeId runtimeId_;
442
+ const bool enableLocking_;
231
443
  const std::shared_ptr<std::recursive_mutex> runtimeMutex_;
444
+ const bool microtaskQueueEnabled_;
232
445
  const std::shared_ptr<jsi::Runtime> runtime_;
233
446
  std::shared_ptr<JSScheduler> jsScheduler_;
234
447
  const RuntimeData::RuntimeKind runtimeKind_;
package/README.md CHANGED
@@ -13,8 +13,8 @@ npm install @depup/react-native-worklets
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [react-native-worklets](https://www.npmjs.com/package/react-native-worklets) @ 0.11.4 |
17
- | Processed | 2026-08-16 |
16
+ | Original | [react-native-worklets](https://www.npmjs.com/package/react-native-worklets) @ 0.12.2 |
17
+ | Processed | 2026-09-13 |
18
18
  | Smoke test | failed |
19
19
  | Deps updated | 13 |
20
20
 
@@ -22,18 +22,18 @@ npm install @depup/react-native-worklets
22
22
 
23
23
  | Dependency | From | To |
24
24
  |------------|------|-----|
25
- | @babel/generator | ^7.27.1 | ^8.0.0 |
25
+ | @babel/generator | ^7.27.1 | ^8.0.5 |
26
26
  | @babel/plugin-transform-arrow-functions | ^7.27.1 | ^8.0.1 |
27
27
  | @babel/plugin-transform-class-properties | ^7.28.6 | ^8.0.1 |
28
- | @babel/plugin-transform-classes | ^7.28.6 | ^8.0.1 |
28
+ | @babel/plugin-transform-classes | ^7.28.6 | ^8.0.5 |
29
29
  | @babel/plugin-transform-nullish-coalescing-operator | ^7.28.6 | ^8.0.1 |
30
- | @babel/plugin-transform-optional-chaining | ^7.28.6 | ^8.0.1 |
30
+ | @babel/plugin-transform-optional-chaining | ^7.28.6 | ^8.0.5 |
31
31
  | @babel/plugin-transform-shorthand-properties | ^7.27.1 | ^8.0.1 |
32
32
  | @babel/plugin-transform-template-literals | ^7.27.1 | ^8.0.1 |
33
33
  | @babel/plugin-transform-unicode-regex | ^7.27.1 | ^8.0.1 |
34
34
  | @babel/preset-typescript | ^7.28.5 | ^8.0.1 |
35
- | @babel/traverse | ^7.27.1 | ^8.0.4 |
36
- | @babel/types | ^7.27.1 | ^8.0.4 |
35
+ | @babel/traverse | ^7.27.1 | ^8.0.5 |
36
+ | @babel/types | ^7.27.1 | ^8.0.5 |
37
37
  | semver | ^7.7.4 | ^7.8.5 |
38
38
 
39
39
  ---
@@ -8,7 +8,21 @@ plugins {
8
8
  id("com.android.library")
9
9
  id("maven-publish")
10
10
  id("com.diffplug.spotless") version "8.1.0"
11
- id("org.jetbrains.kotlin.android")
11
+ id("org.jetbrains.kotlin.android") apply false
12
+ }
13
+
14
+ fun shouldApplyKotlinAndroidPlugin(): Boolean {
15
+ val agpMajorVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.substringBefore('.').toInt()
16
+ if (agpMajorVersion <= 8) {
17
+ return true
18
+ }
19
+
20
+ val builtInKotlin = providers.gradleProperty("android.builtInKotlin").orNull?.toBoolean() ?: true
21
+ return !builtInKotlin
22
+ }
23
+
24
+ if (shouldApplyKotlinAndroidPlugin()) {
25
+ apply(plugin = "org.jetbrains.kotlin.android")
12
26
  }
13
27
 
14
28
  fun safeExtGet(prop: String, fallback: Any?): Any? =
@@ -159,7 +173,7 @@ apply(from = "./fix-prefab.gradle.kts")
159
173
  apply(from = "./generate-stub-pch.gradle.kts")
160
174
 
161
175
  android {
162
- compileSdk = safeExtGet("compileSdkVersion", 36) as Int
176
+ compileSdk = safeExtGet("compileSdkVersion", 37) as Int
163
177
 
164
178
  namespace = "com.swmansion.worklets"
165
179
 
@@ -274,11 +288,11 @@ android {
274
288
 
275
289
  sourceSets {
276
290
  getByName("main") {
277
- java {
291
+ kotlin {
278
292
  if (FETCH_PREVIEW_ENABLED) {
279
- srcDir("src/networking")
293
+ directories.add("src/networking")
280
294
  } else {
281
- srcDir("src/no-networking")
295
+ directories.add("src/no-networking")
282
296
  }
283
297
  }
284
298
  }
@@ -290,7 +304,7 @@ android {
290
304
  }
291
305
 
292
306
  if (project != rootProject) {
293
- kotlin {
307
+ extensions.configure<org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension> {
294
308
  compilerOptions {
295
309
  jvmTarget = JvmTarget.fromTarget("17")
296
310
  }
@@ -7,18 +7,25 @@ namespace worklets {
7
7
  using namespace facebook;
8
8
  using namespace react;
9
9
 
10
- static thread_local bool tls_isOnUIThread = false;
11
-
12
10
  class UISchedulerWrapper : public UIScheduler {
13
11
  private:
14
12
  jni::global_ref<AndroidUIScheduler::javaobject> androidUiScheduler_;
15
13
 
14
+ bool queryIsOnUIThread() const override {
15
+ static const auto method = androidUiScheduler_->getClass()->getMethod<jboolean()>("isOnUIThread");
16
+ return method(androidUiScheduler_);
17
+ }
18
+
16
19
  public:
17
20
  explicit UISchedulerWrapper(jni::global_ref<AndroidUIScheduler::javaobject> androidUiScheduler)
18
21
  : androidUiScheduler_(std::move(androidUiScheduler)) {}
19
22
 
23
+ void triggerUI() override {
24
+ UIScheduler::triggerUI();
25
+ }
26
+
20
27
  void scheduleOnUI(std::function<void()> job) override {
21
- if (tls_isOnUIThread) {
28
+ if (isOnUIThread()) {
22
29
  job();
23
30
  return;
24
31
  }
@@ -29,11 +36,6 @@ class UISchedulerWrapper : public UIScheduler {
29
36
  method(androidUiScheduler_);
30
37
  }
31
38
  }
32
-
33
- void triggerUI() override {
34
- tls_isOnUIThread = true;
35
- UIScheduler::triggerUI();
36
- }
37
39
  };
38
40
 
39
41
  AndroidUIScheduler::AndroidUIScheduler(const jni::alias_ref<AndroidUIScheduler::jhybridobject> &jThis)