diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp index e00ad5fb785..82f18533a9d 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp @@ -25,6 +25,14 @@ void EventBeat::request() const { isRequested_ = true; } +void EventBeat::requestSynchronous() const { + react_native_assert( + beatCallback_ && + "Unexpected state: EventBeat::setBeatCallback was not called before EventBeat::requestSynchronous."); + isSynchronousRequested_ = true; + request(); +} + void EventBeat::setBeatCallback(BeatCallback beatCallback) { beatCallback_ = std::move(beatCallback); } @@ -37,7 +45,7 @@ void EventBeat::induce() const { isRequested_ = false; isBeatCallbackScheduled_ = true; - runtimeScheduler_.scheduleWork( + auto beat = std::function( [this, ownerBox = ownerBox_](jsi::Runtime& runtime) { auto owner = ownerBox->owner.lock(); if (!owner) { @@ -49,6 +57,13 @@ void EventBeat::induce() const { beatCallback_(runtime); } }); + + if (isSynchronousRequested_) { + isSynchronousRequested_ = false; + runtimeScheduler_.executeNowOnTheSameThread(std::move(beat)); + } else { + runtimeScheduler_.scheduleWork(std::move(beat)); + } } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h index a03a9f7cb87..02a0499427e 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h @@ -24,6 +24,9 @@ namespace facebook::react { * Event Beat serves two interleaving purposes: synchronization of event queues * and ensuring that event dispatching happens on proper threads. * + * You must set beat callback by calling `EventBeat::setBeatCallback` before + * calling `EventBeat::request` and `EventBeat::requestSynchronous`. + * * EventBeat is meant to be subclassed by platform-specific implementations. * The platform-specific implementation must call EventBeat::induce(). The * appropriate time to call induce is when the host platform events were queued @@ -68,14 +71,53 @@ class EventBeat { EventBeat& operator=(const EventBeat& other) = delete; /* - * Communicates to the Beat that a consumer is waiting for the coming beat. - * A consumer must request coming beat after the previous beat happened - * to receive a next coming one. + * Communicates to the Beat that a consumer (for example EventQueue) is + * waiting for the coming beat. A consumer must request coming beat after the + * previous beat happened to receive a next coming one. + * + * Callback, that was set by `setBeatCallback`, will be dispatched to + * JavaScript thread asynchronously via `RuntimeScheduler::scheduleWork`. + * + * tick (provided by host platform) + * UI │ + * thread─┬───────┬─▼──────┬─────────────────────────────▶ + * │request│ │induce│ ┌────────────┐ + * └───────┘ └──────┴─▶│scheduleWork│ + * └───────────┬┘ + * JS │ + * thread─────────────────────────────────▼─────────────┬▶ + * │beat callback│ + * └─────────────┘ + * */ virtual void request() const; /* - * The callback is must be called on the proper thread. + * Communicates to the Beat that a consumer (for example EventQueue) is + * waiting for the coming beat. A consumer must request coming beat after the + * previous beat happened to receive a next coming one. + * + * Callback, that was set by `setBeatCallback`, will be called on the thread + * where `induce` is called synchronously via via + * `RuntimeScheduler::executeNowOnTheSameThread`. Both threads are blocked + * until beat callback finishes. + * + * tick + * UI │ + * thread─┬───────┬─▼──────┬─┬─────────────────────────┬▶ + * │request│ │induce│ │executeNowOnTheSameThread│ + * └───────┘ └──────┴▶├─────────────────────────┤ + * JS │ beat callback │ + * thread────────────────────┴─────────────────────────┴▶ + * Both JS and UI thread are + * blocked. + */ + virtual void requestSynchronous() const; + + /* + * The callback will be executed once a consumer (for example EventQueue) + * calls either `EventBeat::request` or `EventBeat::requestSynchronous`. The + * callback will be executed on the proper thread. */ void setBeatCallback(BeatCallback beatCallback); @@ -93,6 +135,7 @@ class EventBeat { private: RuntimeScheduler& runtimeScheduler_; mutable std::atomic isBeatCallbackScheduled_{false}; + mutable std::atomic isSynchronousRequested_{false}; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp index 10d6d0b4659..fb18eea2488 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp @@ -18,11 +18,9 @@ namespace facebook::react { EventDispatcher::EventDispatcher( const EventQueueProcessor& eventProcessor, std::unique_ptr eventBeat, - RuntimeScheduler& runtimeScheduler, StatePipe statePipe, std::weak_ptr eventLogger) - : eventQueue_( - EventQueue(eventProcessor, std::move(eventBeat), runtimeScheduler)), + : eventQueue_(EventQueue(eventProcessor, std::move(eventBeat))), statePipe_(std::move(statePipe)), eventLogger_(std::move(eventLogger)) {} diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h index 79eab45e120..c103e489822 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h @@ -33,7 +33,6 @@ class EventDispatcher { EventDispatcher( const EventQueueProcessor& eventProcessor, std::unique_ptr eventBeat, - RuntimeScheduler& runtimeScheduler, StatePipe statePipe, std::weak_ptr eventLogger); diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp index 4643e5dea27..9290bcc106c 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp @@ -15,11 +15,9 @@ namespace facebook::react { EventQueue::EventQueue( EventQueueProcessor eventProcessor, - std::unique_ptr eventBeat, - RuntimeScheduler& runtimeScheduler) + std::unique_ptr eventBeat) : eventProcessor_(std::move(eventProcessor)), - eventBeat_(std::move(eventBeat)), - runtimeScheduler_(&runtimeScheduler) { + eventBeat_(std::move(eventBeat)) { eventBeat_->setBeatCallback( [this](jsi::Runtime& runtime) { onBeat(runtime); }); } @@ -79,26 +77,14 @@ void EventQueue::enqueueStateUpdate(StateUpdate&& stateUpdate) const { } void EventQueue::onEnqueue() const { - if (synchronousAccessRequested_) { - // Sync flush has been scheduled, no need to request access to the runtime. - return; - } eventBeat_->request(); } void EventQueue::experimental_flushSync() const { - synchronousAccessRequested_ = true; - runtimeScheduler_->executeNowOnTheSameThread([this](jsi::Runtime& runtime) { - synchronousAccessRequested_ = false; - onBeat(runtime); - }); + eventBeat_->requestSynchronous(); } void EventQueue::onBeat(jsi::Runtime& runtime) const { - if (synchronousAccessRequested_) { - // Sync flush has been scheduled, let's yield to it. - return; - } flushStateUpdates(); flushEvents(runtime); } diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h index f82940c431f..3578473b766 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h @@ -29,8 +29,7 @@ class EventQueue { public: EventQueue( EventQueueProcessor eventProcessor, - std::unique_ptr eventBeat, - RuntimeScheduler& runtimeScheduler); + std::unique_ptr eventBeat); /* * Enqueues and (probably later) dispatch a given event. @@ -75,10 +74,6 @@ class EventQueue { mutable std::vector eventQueue_; mutable std::vector stateUpdateQueue_; mutable std::mutex queueMutex_; - - // TODO: T183075253 - RuntimeScheduler* runtimeScheduler_; - mutable std::atomic_bool synchronousAccessRequested_{false}; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 432450ae43f..6950740f71a 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -102,7 +102,6 @@ Scheduler::Scheduler( EventQueueProcessor( eventPipe, eventPipeConclusion, statePipe, eventPerformanceLogger_), std::move(eventBeat), - *runtimeScheduler, statePipe, eventPerformanceLogger_);