131 lines
4.1 KiB
C++
131 lines
4.1 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 <atomic>
|
|
#include <memory>
|
|
#include <mutex>
|
|
|
|
#include <folly/concurrency/UnboundedQueue.h>
|
|
#include <folly/executors/GlobalExecutor.h>
|
|
#include <folly/executors/SequencedExecutor.h>
|
|
#include <folly/io/async/Request.h>
|
|
|
|
namespace folly {
|
|
|
|
/**
|
|
* @class SerialExecutor
|
|
*
|
|
* @brief Executor that guarantees serial non-concurrent execution of added
|
|
* tasks
|
|
*
|
|
* SerialExecutor is similar to boost asio's strand concept. A SerialExecutor
|
|
* has a parent executor which is given at construction time (defaults to
|
|
* folly's global CPUExecutor). Tasks added to SerialExecutor are executed
|
|
* in the parent executor, however strictly non-concurrently and in the order
|
|
* they were added.
|
|
*
|
|
* SerialExecutor tries to schedule its tasks fairly. Every task submitted to
|
|
* it results in one task submitted to the parent executor. Whenever the parent
|
|
* executor executes one of those, one of the tasks submitted to SerialExecutor
|
|
* is marked for execution, which means it will either be executed at once,
|
|
* or if a task is currently being executed already, after that.
|
|
*
|
|
* The SerialExecutor may be deleted at any time. All tasks that have been
|
|
* submitted will still be executed with the same guarantees, as long as the
|
|
* parent executor is executing tasks.
|
|
*/
|
|
|
|
class SerialExecutor : public SequencedExecutor {
|
|
public:
|
|
SerialExecutor(SerialExecutor const&) = delete;
|
|
SerialExecutor& operator=(SerialExecutor const&) = delete;
|
|
SerialExecutor(SerialExecutor&&) = delete;
|
|
SerialExecutor& operator=(SerialExecutor&&) = delete;
|
|
|
|
static KeepAlive<SerialExecutor> create(
|
|
KeepAlive<Executor> parent = getKeepAliveToken(getCPUExecutor().get()));
|
|
|
|
class Deleter {
|
|
public:
|
|
Deleter() {}
|
|
|
|
void operator()(SerialExecutor* executor) {
|
|
executor->keepAliveRelease();
|
|
}
|
|
|
|
private:
|
|
friend class SerialExecutor;
|
|
explicit Deleter(std::shared_ptr<Executor> parent)
|
|
: parent_(std::move(parent)) {}
|
|
|
|
std::shared_ptr<Executor> parent_;
|
|
};
|
|
|
|
using UniquePtr = std::unique_ptr<SerialExecutor, Deleter>;
|
|
[[deprecated("Replaced by create")]] static UniquePtr createUnique(
|
|
std::shared_ptr<Executor> parent = getCPUExecutor());
|
|
|
|
/**
|
|
* Add one task for execution in the parent executor
|
|
*/
|
|
void add(Func func) override;
|
|
|
|
/**
|
|
* Add one task for execution in the parent executor, and use the given
|
|
* priority for one task submission to parent executor.
|
|
*
|
|
* Since in-order execution of tasks submitted to SerialExecutor is
|
|
* guaranteed, the priority given here does not necessarily reflect the
|
|
* execution priority of the task submitted with this call to
|
|
* `addWithPriority`. The given priority is passed on to the parent executor
|
|
* for the execution of one of the SerialExecutor's tasks.
|
|
*/
|
|
void addWithPriority(Func func, int8_t priority) override;
|
|
uint8_t getNumPriorities() const override {
|
|
return parent_->getNumPriorities();
|
|
}
|
|
|
|
protected:
|
|
bool keepAliveAcquire() override;
|
|
|
|
void keepAliveRelease() override;
|
|
|
|
private:
|
|
struct Task {
|
|
Func func;
|
|
std::shared_ptr<RequestContext> ctx;
|
|
};
|
|
|
|
explicit SerialExecutor(KeepAlive<Executor> parent);
|
|
~SerialExecutor() override;
|
|
|
|
void run();
|
|
|
|
KeepAlive<Executor> parent_;
|
|
std::atomic<std::size_t> scheduled_{0};
|
|
/**
|
|
* Unbounded multi producer single consumer queue where consumers don't block
|
|
* on dequeue.
|
|
*/
|
|
folly::UnboundedQueue<Task, false, true, false> queue_;
|
|
|
|
std::atomic<ssize_t> keepAliveCounter_{1};
|
|
};
|
|
|
|
} // namespace folly
|