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
This commit is contained in:
Valentin Shergin 2018-09-14 15:16:51 -07:00 committed by Facebook Github Bot
parent 238dec2787
commit 4eb9b40979
3 changed files with 41 additions and 6 deletions

View File

@ -58,8 +58,8 @@ private:
SharedContextContainer contextContainer = std::make_shared<ContextContainer>(); SharedContextContainer contextContainer = std::make_shared<ContextContainer>();
EventBeatFactory synchronousBeatFactory = []() { EventBeatFactory synchronousBeatFactory = [bridge]() {
return std::make_unique<MainRunLoopEventBeat>(); return std::make_unique<MainRunLoopEventBeat>(bridge.jsMessageThread);
}; };
EventBeatFactory asynchronousBeatFactory = [bridge]() { EventBeatFactory asynchronousBeatFactory = [bridge]() {

View File

@ -7,6 +7,7 @@
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRunLoop.h> #include <CoreFoundation/CFRunLoop.h>
#include <cxxreact/MessageQueueThread.h>
#include <fabric/events/EventBeat.h> #include <fabric/events/EventBeat.h>
namespace facebook { namespace facebook {
@ -20,12 +21,15 @@ class MainRunLoopEventBeat final:
public EventBeat { public EventBeat {
public: public:
MainRunLoopEventBeat(); MainRunLoopEventBeat(std::shared_ptr<MessageQueueThread> messageQueueThread);
~MainRunLoopEventBeat(); ~MainRunLoopEventBeat();
void induce() const override; void induce() const override;
private: private:
void blockMessageQueueAndThenBeat() const;
std::shared_ptr<MessageQueueThread> messageQueueThread_;
CFRunLoopObserverRef mainRunLoopObserver_; CFRunLoopObserverRef mainRunLoopObserver_;
}; };

View File

@ -5,12 +5,15 @@
#import "MainRunLoopEventBeat.h" #import "MainRunLoopEventBeat.h"
#import <mutex>
#import <React/RCTUtils.h> #import <React/RCTUtils.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
MainRunLoopEventBeat::MainRunLoopEventBeat() { MainRunLoopEventBeat::MainRunLoopEventBeat(std::shared_ptr<MessageQueueThread> messageQueueThread):
messageQueueThread_(std::move(messageQueueThread)) {
mainRunLoopObserver_ = mainRunLoopObserver_ =
CFRunLoopObserverCreateWithHandler( CFRunLoopObserverCreateWithHandler(
NULL /* allocator */, NULL /* allocator */,
@ -18,7 +21,11 @@ MainRunLoopEventBeat::MainRunLoopEventBeat() {
true /* repeats */, true /* repeats */,
0 /* order */, 0 /* order */,
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
this->beat(); if (!this->isRequested_) {
return;
}
this->blockMessageQueueAndThenBeat();
} }
); );
@ -38,9 +45,33 @@ void MainRunLoopEventBeat::induce() const {
} }
RCTExecuteOnMainQueue(^{ 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 react
} // namespace facebook } // namespace facebook