Fabric: Introducing EventBeat concept (and MainRunLoopEventBeat)

Summary:
@public
EventBeat is an abstraction around proper event scheduling combining proper timing and proper threading. Event Queues use Event Beat to ensure that events are delivered on proper threads and in proper timing (probably batched). Consumers can `request` the next beat and `induce` immediatly beat.
MainRunLoopEventBeat implements particular Event Beat synchronized with the main event loop and calling a callback on the main thread.

Reviewed By: mdvacca

Differential Revision: D8886229

fbshipit-source-id: 1a42fcbf4cd61c6cb4c502890566c98b00226f31
This commit is contained in:
Valentin Shergin 2018-08-27 07:21:05 -07:00 committed by Facebook Github Bot
parent 122dc37afe
commit 57bbce9bd9
4 changed files with 184 additions and 0 deletions

View File

@ -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 <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRunLoop.h>
#include <fabric/events/EventBeat.h>
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

View File

@ -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 <React/RCTUtils.h>
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

View File

@ -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

View File

@ -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 <atomic>
#include <functional>
#include <memory>
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<void()>;
/*
* 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<bool> isRequested_ {false};
};
using EventBeatFactory = std::function<std::unique_ptr<EventBeat>()>;
} // namespace react
} // namespace facebook