From 4eb9b409790628f6356a60ddf860159dc4a9e75a Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 14 Sep 2018 15:16:51 -0700 Subject: [PATCH] Fabric: Proper thread synchronization in MainRunLoopEventBeat Summary: To dispatch events synchonously on the main thread we still have to block the Message Queue. We actually dispatch events on the Message Queue while the main thread is waiting. Reviewed By: mdvacca Differential Revision: D9799853 fbshipit-source-id: 8033be36f27371ad2f1dc7210d564fbca1174910 --- React/Fabric/RCTScheduler.mm | 4 +-- React/Fabric/Utils/MainRunLoopEventBeat.h | 6 +++- React/Fabric/Utils/MainRunLoopEventBeat.mm | 37 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/React/Fabric/RCTScheduler.mm b/React/Fabric/RCTScheduler.mm index d7e81c425..8af8a92cd 100644 --- a/React/Fabric/RCTScheduler.mm +++ b/React/Fabric/RCTScheduler.mm @@ -58,8 +58,8 @@ private: SharedContextContainer contextContainer = std::make_shared(); - EventBeatFactory synchronousBeatFactory = []() { - return std::make_unique(); + EventBeatFactory synchronousBeatFactory = [bridge]() { + return std::make_unique(bridge.jsMessageThread); }; EventBeatFactory asynchronousBeatFactory = [bridge]() { diff --git a/React/Fabric/Utils/MainRunLoopEventBeat.h b/React/Fabric/Utils/MainRunLoopEventBeat.h index 8bf0aea8b..abfd8c230 100644 --- a/React/Fabric/Utils/MainRunLoopEventBeat.h +++ b/React/Fabric/Utils/MainRunLoopEventBeat.h @@ -7,6 +7,7 @@ #include #include +#include #include namespace facebook { @@ -20,12 +21,15 @@ class MainRunLoopEventBeat final: public EventBeat { public: - MainRunLoopEventBeat(); + MainRunLoopEventBeat(std::shared_ptr messageQueueThread); ~MainRunLoopEventBeat(); void induce() const override; private: + void blockMessageQueueAndThenBeat() const; + + std::shared_ptr messageQueueThread_; CFRunLoopObserverRef mainRunLoopObserver_; }; diff --git a/React/Fabric/Utils/MainRunLoopEventBeat.mm b/React/Fabric/Utils/MainRunLoopEventBeat.mm index dcf67c7a1..33e8e9a38 100644 --- a/React/Fabric/Utils/MainRunLoopEventBeat.mm +++ b/React/Fabric/Utils/MainRunLoopEventBeat.mm @@ -5,12 +5,15 @@ #import "MainRunLoopEventBeat.h" +#import #import namespace facebook { namespace react { -MainRunLoopEventBeat::MainRunLoopEventBeat() { +MainRunLoopEventBeat::MainRunLoopEventBeat(std::shared_ptr messageQueueThread): + messageQueueThread_(std::move(messageQueueThread)) { + mainRunLoopObserver_ = CFRunLoopObserverCreateWithHandler( NULL /* allocator */, @@ -18,7 +21,11 @@ MainRunLoopEventBeat::MainRunLoopEventBeat() { true /* repeats */, 0 /* order */, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { - this->beat(); + if (!this->isRequested_) { + return; + } + + this->blockMessageQueueAndThenBeat(); } ); @@ -38,9 +45,33 @@ void MainRunLoopEventBeat::induce() const { } RCTExecuteOnMainQueue(^{ - this->beat(); + this->blockMessageQueueAndThenBeat(); }); } +void MainRunLoopEventBeat::blockMessageQueueAndThenBeat() const { + // Note: We need the third mutex to get back to the main thread before + // the lambda is finished (because all mutexes are allocated on the stack). + + std::mutex mutex1; + std::mutex mutex2; + std::mutex mutex3; + + mutex1.lock(); + mutex2.lock(); + mutex3.lock(); + + messageQueueThread_->runOnQueue([&]() { + mutex1.unlock(); + mutex2.lock(); + mutex3.unlock(); + }); + + mutex1.lock(); + beat(); + mutex2.unlock(); + mutex3.lock(); +} + } // namespace react } // namespace facebook