diff --git a/Libraries/Renderer/implementations/ReactFabric-dev.fb.js b/Libraries/Renderer/implementations/ReactFabric-dev.fb.js index e3e5f40045e..3c37320dbd2 100644 --- a/Libraries/Renderer/implementations/ReactFabric-dev.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-dev.fb.js @@ -24603,6 +24603,6 @@ if ( ) { __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error()); } - + })(); } diff --git a/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp b/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp index ecf2589dddc..b25efc0a5ac 100644 --- a/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +++ b/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp @@ -58,6 +58,10 @@ bool RuntimeScheduler::getShouldYield() const noexcept { return shouldYield_; } +bool RuntimeScheduler::getIsSynchronous() const noexcept { + return isSynchronous_; +} + void RuntimeScheduler::cancelTask(const std::shared_ptr &task) noexcept { task->callback.reset(); } @@ -75,14 +79,31 @@ void RuntimeScheduler::setEnableYielding(bool enableYielding) { } void RuntimeScheduler::executeNowOnTheSameThread( - std::function callback) const { + std::function callback) { shouldYield_ = true; executeSynchronouslyOnSameThread_CAN_DEADLOCK( runtimeExecutor_, - [callback = std::move(callback)](jsi::Runtime &runtime) { - callback(runtime); + [this, callback = std::move(callback)](jsi::Runtime &runtime) { + shouldYield_ = false; + auto task = jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forUtf8(runtime, ""), + 3, + [callback = std::move(callback)]( + jsi::Runtime &runtime, + jsi::Value const &, + jsi::Value const *arguments, + size_t) -> jsi::Value { + callback(runtime); + return jsi::Value::undefined(); + }); + assert(!isPerformingWork_); + this->scheduleTask( + SchedulerPriority::ImmediatePriority, std::move(task)); + isSynchronous_ = true; + startWorkLoop(runtime); + isSynchronous_ = false; }); - shouldYield_ = false; } #pragma mark - Private diff --git a/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h b/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h index 5d18a7d9ea8..1cd6a4c3f3f 100644 --- a/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h +++ b/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h @@ -45,8 +45,14 @@ class RuntimeScheduler final { * component. */ void executeNowOnTheSameThread( - std::function callback) const; + std::function callback); + /* + * Adds a JavaScript callback to priority queue with given priority. + * Triggers workloop if needed. + * + * Thread synchronization must be enforced externally. + */ std::shared_ptr scheduleTask( SchedulerPriority priority, jsi::Function callback); @@ -55,6 +61,8 @@ class RuntimeScheduler final { bool getShouldYield() const noexcept; + bool getIsSynchronous() const noexcept; + SchedulerPriority getCurrentPriorityLevel() const noexcept; RuntimeSchedulerTimePoint now() const noexcept; @@ -71,6 +79,7 @@ class RuntimeScheduler final { RuntimeExecutor const runtimeExecutor_; mutable SchedulerPriority currentPriority_{SchedulerPriority::NormalPriority}; mutable std::atomic_bool shouldYield_{false}; + mutable std::atomic_bool isSynchronous_{false}; void startWorkLoop(jsi::Runtime &runtime) const; @@ -84,7 +93,7 @@ class RuntimeScheduler final { * Flag indicating if callback on JavaScript queue has been * scheduled. */ - std::atomic_bool isCallbackScheduled_{false}; + mutable std::atomic_bool isCallbackScheduled_{false}; /* * Flag indicating if yielding is enabled. diff --git a/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp b/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp index 92525118e28..e68b479f5e1 100644 --- a/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp +++ b/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp @@ -42,10 +42,28 @@ RuntimeSchedulerBinding::createAndInstallIfNeeded( return runtimeSchedulerObject.getHostObject(runtime); } +std::shared_ptr RuntimeSchedulerBinding::getBinding( + jsi::Runtime &runtime) { + auto runtimeSchedulerModuleName = "nativeRuntimeScheduler"; + + auto runtimeSchedulerValue = + runtime.global().getProperty(runtime, runtimeSchedulerModuleName); + if (runtimeSchedulerValue.isUndefined()) { + return nullptr; + } + + auto runtimeSchedulerObject = runtimeSchedulerValue.asObject(runtime); + return runtimeSchedulerObject.getHostObject(runtime); +} + RuntimeSchedulerBinding::RuntimeSchedulerBinding( std::shared_ptr const &runtimeScheduler) : runtimeScheduler_(runtimeScheduler) {} +bool RuntimeSchedulerBinding::getIsSynchronous() const { + return runtimeScheduler_->getIsSynchronous(); +} + jsi::Value RuntimeSchedulerBinding::get( jsi::Runtime &runtime, jsi::PropNameID const &name) { diff --git a/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h b/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h index 27c1ad9f31d..ccb3fb7f261 100644 --- a/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h +++ b/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h @@ -31,11 +31,20 @@ class RuntimeSchedulerBinding : public jsi::HostObject { jsi::Runtime &runtime, std::shared_ptr const &runtimeScheduler); + /* + * Returns a shared pointer to RuntimeSchedulerBinding previously installed + * into a runtime. Thread synchronization must be enforced externally. + */ + static std::shared_ptr getBinding( + jsi::Runtime &runtime); + /* * `jsi::HostObject` specific overloads. */ jsi::Value get(jsi::Runtime &runtime, jsi::PropNameID const &name) override; + bool getIsSynchronous() const; + private: std::shared_ptr runtimeScheduler_; }; diff --git a/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h b/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h index 329fe252aa3..2639705f7f4 100644 --- a/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h +++ b/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h @@ -61,5 +61,21 @@ static inline std::chrono::milliseconds timeoutForSchedulerPriority( } } +static inline std::string debugValueForSchedulerPriority( + SchedulerPriority schedulerPriority) { + switch (schedulerPriority) { + case SchedulerPriority::ImmediatePriority: + return "SchedulerPriority::ImmediatePriority"; + case SchedulerPriority::UserBlockingPriority: + return "SchedulerPriority::UserBlockingPriority"; + case SchedulerPriority::NormalPriority: + return "SchedulerPriority::NormalPriority"; + case SchedulerPriority::LowPriority: + return "SchedulerPriority::LowPriority"; + case SchedulerPriority::IdlePriority: + return "SchedulerPriority::IdlePriority"; + } +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/uimanager/Android.mk b/ReactCommon/react/renderer/uimanager/Android.mk index fb1d9c2e9b8..fe59da25ea9 100644 --- a/ReactCommon/react/renderer/uimanager/Android.mk +++ b/ReactCommon/react/renderer/uimanager/Android.mk @@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \ libreact_render_debug \ libreact_render_graphics \ libreact_render_leakchecker \ + libreact_render_runtimescheduler \ libreact_render_mounting \ libreact_config \ librrc_root \ @@ -49,6 +50,7 @@ $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/componentregistry) $(call import-module,react/renderer/core) $(call import-module,react/renderer/leakchecker) +$(call import-module,react/renderer/runtimescheduler) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/mounting) diff --git a/ReactCommon/react/renderer/uimanager/BUCK b/ReactCommon/react/renderer/uimanager/BUCK index d518a76c385..9633c00bdec 100644 --- a/ReactCommon/react/renderer/uimanager/BUCK +++ b/ReactCommon/react/renderer/uimanager/BUCK @@ -60,6 +60,7 @@ rn_xplat_cxx_library( react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + react_native_xplat_target("react/renderer/runtimescheduler:runtimescheduler"), ], ) diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index e76348b74f6..b604a3fe3ab 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace facebook::react { @@ -543,33 +544,47 @@ jsi::Value UIManagerBinding::get( jsi::Value const &thisValue, jsi::Value const *arguments, size_t count) noexcept -> jsi::Value { + auto runtimeSchedulerBinding = + RuntimeSchedulerBinding::getBinding(runtime); auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); - auto weakShadowNodeList = - weakShadowNodeListFromValue(runtime, arguments[1]); - static std::atomic_uint_fast8_t completeRootEventCounter{0}; - static std::atomic_uint_fast32_t mostRecentSurfaceId{0}; - completeRootEventCounter += 1; - mostRecentSurfaceId = surfaceId; - uiManager->backgroundExecutor_( - [weakUIManager, - weakShadowNodeList, - surfaceId, - eventCount = completeRootEventCounter.load()] { - auto shouldYield = [=]() -> bool { - // If `completeRootEventCounter` was incremented, another - // `completeSurface` call has been scheduled and current - // `completeSurface` should yield to it. - return completeRootEventCounter > eventCount && - mostRecentSurfaceId == surfaceId; - }; - auto shadowNodeList = - shadowNodeListFromWeakList(weakShadowNodeList); - auto strongUIManager = weakUIManager.lock(); - if (shadowNodeList && strongUIManager) { - strongUIManager->completeSurface( - surfaceId, shadowNodeList, {true, shouldYield}); - } - }); + + if (runtimeSchedulerBinding && + runtimeSchedulerBinding->getIsSynchronous()) { + auto weakShadowNodeList = + weakShadowNodeListFromValue(runtime, arguments[1]); + auto shadowNodeList = + shadowNodeListFromWeakList(weakShadowNodeList); + if (shadowNodeList) { + uiManager->completeSurface(surfaceId, shadowNodeList, {true}); + } + } else { + auto weakShadowNodeList = + weakShadowNodeListFromValue(runtime, arguments[1]); + static std::atomic_uint_fast8_t completeRootEventCounter{0}; + static std::atomic_uint_fast32_t mostRecentSurfaceId{0}; + completeRootEventCounter += 1; + mostRecentSurfaceId = surfaceId; + uiManager->backgroundExecutor_( + [weakUIManager, + weakShadowNodeList, + surfaceId, + eventCount = completeRootEventCounter.load()] { + auto shouldYield = [=]() -> bool { + // If `completeRootEventCounter` was incremented, another + // `completeSurface` call has been scheduled and current + // `completeSurface` should yield to it. + return completeRootEventCounter > eventCount && + mostRecentSurfaceId == surfaceId; + }; + auto shadowNodeList = + shadowNodeListFromWeakList(weakShadowNodeList); + auto strongUIManager = weakUIManager.lock(); + if (shadowNodeList && strongUIManager) { + strongUIManager->completeSurface( + surfaceId, shadowNodeList, {true, shouldYield}); + } + }); + } return jsi::Value::undefined(); });