vn-verdnaturachat/ios/Pods/Flipper-Folly/folly/executors/ManualExecutor.h

175 lines
4.8 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstdio>
#include <memory>
#include <mutex>
#include <queue>
#include <folly/executors/DrivableExecutor.h>
#include <folly/executors/ScheduledExecutor.h>
#include <folly/executors/SequencedExecutor.h>
#include <folly/synchronization/LifoSem.h>
namespace folly {
/// A ManualExecutor only does work when you turn the crank, by calling
/// run() or indirectly with makeProgress() or waitFor().
///
/// The clock for a manual executor starts at 0 and advances only when you
/// ask it to. i.e. time is also under manual control.
///
/// NB No attempt has been made to make anything other than add and schedule
/// threadsafe.
class ManualExecutor : public DrivableExecutor,
public ScheduledExecutor,
public SequencedExecutor {
public:
~ManualExecutor();
void add(Func) override;
/// Do work. Returns the number of functions that were executed (maybe 0).
/// Non-blocking, in the sense that we don't wait for work (we can't
/// control whether one of the functions blocks).
/// This is stable, it will not chase an ever-increasing tail of work.
/// This also means, there may be more work available to perform at the
/// moment that this returns.
size_t run();
// Do work until there is no more work to do.
// Returns the number of functions that were executed (maybe 0).
// Unlike run, this method is not stable. It will chase an infinite tail of
// work so should be used with care.
// There will be no work available to perform at the moment that this
// returns.
size_t drain();
/// Wait for work to do.
void wait();
/// Wait for work to do, and do it.
void makeProgress() {
wait();
run();
}
/// Implements DrivableExecutor
void drive() override {
makeProgress();
}
/// makeProgress until this Future is ready.
template <class F>
void waitFor(F const& f) {
// TODO(5427828)
#if 0
while (!f.isReady())
makeProgress();
#else
while (!f.isReady()) {
run();
}
#endif
}
void scheduleAt(Func&& f, TimePoint const& t) override {
std::lock_guard<std::mutex> lock(lock_);
scheduledFuncs_.emplace(t, std::move(f));
sem_.post();
}
/// Advance the clock. The clock never advances on its own.
/// Advancing the clock causes some work to be done, if work is available
/// to do (perhaps newly available because of the advanced clock).
/// If dur is <= 0 this is a noop.
void advance(Duration const& dur) {
advanceTo(now_ + dur);
}
/// Advance the clock to this absolute time. If t is <= now(),
/// this is a noop.
void advanceTo(TimePoint const& t);
TimePoint now() override {
return now_;
}
/// Flush the function queue. Destroys all stored functions without
/// executing them. Returns number of removed functions.
std::size_t clear() {
std::queue<Func> funcs;
std::priority_queue<ScheduledFunc> scheduled_funcs;
{
std::lock_guard<std::mutex> lock(lock_);
funcs_.swap(funcs);
scheduledFuncs_.swap(scheduled_funcs);
}
return funcs.size() + scheduled_funcs.size();
}
bool keepAliveAcquire() override {
keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
return true;
}
void keepAliveRelease() override {
if (keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
add([] {});
}
}
private:
std::mutex lock_;
std::queue<Func> funcs_;
LifoSem sem_;
// helper class to enable ordering of scheduled events in the priority
// queue
struct ScheduledFunc {
TimePoint time;
size_t ordinal;
Func mutable func;
ScheduledFunc(TimePoint const& t, Func&& f) : time(t), func(std::move(f)) {
static size_t seq = 0;
ordinal = seq++;
}
bool operator<(ScheduledFunc const& b) const {
// Earlier-scheduled things must be *higher* priority
// in the max-based std::priority_queue
if (time == b.time) {
return ordinal > b.ordinal;
}
return time > b.time;
}
Func&& moveOutFunc() const {
return std::move(func);
}
};
std::priority_queue<ScheduledFunc> scheduledFuncs_;
TimePoint now_ = TimePoint::min();
std::atomic<ssize_t> keepAliveCount_{0};
};
} // namespace folly