From bf8d6da101a3fd56f656caeda93e7619f0e19387 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 11 Apr 2025 03:05:00 -0700 Subject: [PATCH] Make Task.call API simpler (#50545) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50545 All usages of Task.call involve a Runnable which returns another Task, which we then extract using `.continueWithTask(Task::getResult)`. Instead inline this behaviour inside `Task.call` to avoid allocation of unnecessary intermediate objects. Changelog: [Internal] Reviewed By: rshest Differential Revision: D72600051 fbshipit-source-id: 7e7c0c8e1de24b1aeff7addab2eb10272bc73ddb --- .../facebook/react/runtime/ReactHostImpl.java | 87 +++++++++---------- .../react/runtime/internal/bolts/Task.kt | 21 ++++- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java index 5543c218d32..3a6c94a16a5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java @@ -231,7 +231,7 @@ public class ReactHostImpl implements ReactHost { */ @Override public TaskInterface start() { - return Task.call(this::getOrCreateStartTask, mBGExecutor).continueWithTask(Task::getResult); + return Task.call(this::getOrCreateStartTask, mBGExecutor); } /** Initialize and run a React Native surface in a background without mounting real views. */ @@ -488,37 +488,36 @@ public class ReactHostImpl implements ReactHost { public TaskInterface reload(String reason) { final String method = "reload()"; return Task.call( - () -> { - Task reloadTask = null; - if (mDestroyTask != null) { - log(method, "Waiting for destroy to finish, before reloading React Native."); - reloadTask = - mDestroyTask - .continueWithTask(task -> getOrCreateReloadTask(reason), mBGExecutor) - .makeVoid(); - } else { - reloadTask = getOrCreateReloadTask(reason).makeVoid(); - } + () -> { + Task reloadTask = null; + if (mDestroyTask != null) { + log(method, "Waiting for destroy to finish, before reloading React Native."); + reloadTask = + mDestroyTask + .continueWithTask(task -> getOrCreateReloadTask(reason), mBGExecutor) + .makeVoid(); + } else { + reloadTask = getOrCreateReloadTask(reason).makeVoid(); + } - return reloadTask.continueWithTask( - task -> { - if (task.isFaulted()) { - final Exception ex = task.getError(); - Assertions.assertNotNull(ex, "Reload failed without an exception"); - if (mUseDevSupport) { - mDevSupportManager.handleException(ex); - } else { - mReactHostDelegate.handleInstanceException(ex); - } - return getOrCreateDestroyTask("Reload failed", ex); - } + return reloadTask.continueWithTask( + task -> { + if (task.isFaulted()) { + final Exception ex = task.getError(); + Assertions.assertNotNull(ex, "Reload failed without an exception"); + if (mUseDevSupport) { + mDevSupportManager.handleException(ex); + } else { + mReactHostDelegate.handleInstanceException(ex); + } + return getOrCreateDestroyTask("Reload failed", ex); + } - return task; - }, - mBGExecutor); - }, - mBGExecutor) - .continueWithTask(Task::getResult); + return task; + }, + mBGExecutor); + }, + mBGExecutor); } @DoNotStrip @@ -601,19 +600,18 @@ public class ReactHostImpl implements ReactHost { public TaskInterface destroy(String reason, @Nullable Exception ex) { final String method = "destroy()"; return Task.call( - () -> { - if (mReloadTask != null) { - log( - method, - "Reloading React Native. Waiting for reload to finish before destroying React" - + " Native."); - return mReloadTask.continueWithTask( - task -> getOrCreateDestroyTask(reason, ex), mBGExecutor); - } - return getOrCreateDestroyTask(reason, ex); - }, - mBGExecutor) - .continueWithTask(Task::getResult); + () -> { + if (mReloadTask != null) { + log( + method, + "Reloading React Native. Waiting for reload to finish before destroying React" + + " Native."); + return mReloadTask.continueWithTask( + task -> getOrCreateDestroyTask(reason, ex), mBGExecutor); + } + return getOrCreateDestroyTask(reason, ex); + }, + mBGExecutor); } private MemoryPressureListener createMemoryPressureListener(ReactInstance reactInstance) { @@ -1111,8 +1109,7 @@ public class ReactHostImpl implements ReactHost { * destroying, will wait until destroy is finished, before creating. */ private Task getOrCreateReactInstance() { - return Task.call(this::waitThenCallGetOrCreateReactInstanceTask, mBGExecutor) - .continueWithTask(Task::getResult); + return Task.call(this::waitThenCallGetOrCreateReactInstanceTask, mBGExecutor); } @ThreadConfined("ReactHost") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.kt index 0aa955ab070..5b46c748563 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.kt @@ -297,12 +297,29 @@ public class Task : TaskInterface { * Invokes the callable using the given executor, returning a Task to represent the operation. */ @JvmStatic - public fun call(callable: Callable, executor: Executor): Task { + public fun call( + callable: Callable>, + executor: Executor + ): Task { val tcs = TaskCompletionSource() try { executor.execute { + val continuation = Continuation { task: Task -> + when { + task.isCancelled() -> tcs.setCancelled() + task.isFaulted() -> tcs.setError(task.getError()) + else -> tcs.setResult(task.getResult()) + } + } try { - tcs.setResult(callable.call()) + val task = callable.call() + synchronized(task.lock) { + if (task.isCompleted()) { + continuation.then(task) + } else { + task.continuations.add(continuation) + } + } } catch (e: CancellationException) { tcs.setCancelled() } catch (e: Exception) {