diff --git a/React/Fabric/Utils/MainRunLoopEventBeat.h b/React/Fabric/Utils/MainRunLoopEventBeat.h new file mode 100644 index 000000000..cdeb1e89b --- /dev/null +++ b/React/Fabric/Utils/MainRunLoopEventBeat.h @@ -0,0 +1,33 @@ +// Copyright (c) 2004-present, Facebook, Inc. + +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * Event beat associated with main run loop cycle. + * The callback is always called on the main thread. + */ +class MainRunLoopEventBeat final: + public EventBeat { + +public: + MainRunLoopEventBeat(); + ~MainRunLoopEventBeat(); + + void induce() const override; + +private: + CFRunLoopObserverRef mainRunLoopObserver_; +}; + +} // namespace react +} // namespace facebook diff --git a/React/Fabric/Utils/MainRunLoopEventBeat.mm b/React/Fabric/Utils/MainRunLoopEventBeat.mm new file mode 100644 index 000000000..4d6ca934c --- /dev/null +++ b/React/Fabric/Utils/MainRunLoopEventBeat.mm @@ -0,0 +1,46 @@ +// Copyright (c) 2004-present, Facebook, Inc. + +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +#import "MainRunLoopEventBeat.h" + +#import + +namespace facebook { +namespace react { + +MainRunLoopEventBeat::MainRunLoopEventBeat() { + mainRunLoopObserver_ = + CFRunLoopObserverCreateWithHandler( + NULL /* allocator */, + kCFRunLoopBeforeWaiting /* activities */, + true /* repeats */, + 0 /* order */, + ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + this->beat(); + } + ); + + assert(mainRunLoopObserver_); + + CFRunLoopAddObserver(CFRunLoopGetMain(), mainRunLoopObserver_, kCFRunLoopCommonModes); +} + +MainRunLoopEventBeat::~MainRunLoopEventBeat() { + CFRunLoopRemoveObserver(CFRunLoopGetMain(), mainRunLoopObserver_, kCFRunLoopCommonModes); + CFRelease(mainRunLoopObserver_); +} + +void MainRunLoopEventBeat::induce() const { + if (!this->isRequested_) { + return; + } + + RCTExecuteOnMainQueue(^{ + this->beat(); + }); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/events/EventBeat.cpp b/ReactCommon/fabric/events/EventBeat.cpp new file mode 100644 index 000000000..e86d95997 --- /dev/null +++ b/ReactCommon/fabric/events/EventBeat.cpp @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "EventBeat.h" + +namespace facebook { +namespace react { + +void EventBeat::request() const { + isRequested_ = true; +} + +void EventBeat::beat() const { + if (!this->isRequested_) { + return; + } + + if (!beatCallback_) { + return; + } + + beatCallback_(); + isRequested_ = false; +} + +void EventBeat::induce() const { + // Default implementation does nothing. +} + +void EventBeat::setBeatCallback(const BeatCallback &beatCallback) { + beatCallback_ = beatCallback; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/events/EventBeat.h b/ReactCommon/fabric/events/EventBeat.h new file mode 100644 index 000000000..cdd68a70a --- /dev/null +++ b/ReactCommon/fabric/events/EventBeat.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * Event Beat serves two interleaving purposes: synchronization of event queues and + * ensuring that event dispatching happens on propper threads. + */ +class EventBeat { + +public: + virtual ~EventBeat() = default; + + using BeatCallback = std::function; + + /* + * 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. + */ + virtual void request() const; + + /* + * Induces the next beat to happen as soon as possible. If the method + * is called on the proper thread, the beat must happen synchronously. + * Subclasses might override this method to implement specific + * out-of-turn beat scheduling. + * Some types of Event Beats do not support inducing, hence the default + * implementation does nothing. + * Receiver might ignore the call if a beat was not requested. + */ + virtual void induce() const; + + /* + * Sets a callback function. + * The callback is must be called on the proper thread. + */ + void setBeatCallback(const BeatCallback &beatCallback); + +protected: + /* + * Should be used by sublasses to send a beat. + * Receiver might ignore the call if a beat was not requested. + */ + void beat() const; + + BeatCallback beatCallback_; + mutable std::atomic isRequested_ {false}; +}; + +using EventBeatFactory = std::function()>; + +} // namespace react +} // namespace facebook