1/*
2 * Copyright 2017-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <atomic>
20#include <memory>
21#include <mutex>
22
23#include <folly/concurrency/UnboundedQueue.h>
24#include <folly/executors/GlobalExecutor.h>
25#include <folly/executors/SequencedExecutor.h>
26
27namespace folly {
28
29/**
30 * @class SerialExecutor
31 *
32 * @brief Executor that guarantees serial non-concurrent execution of added
33 * tasks
34 *
35 * SerialExecutor is similar to boost asio's strand concept. A SerialExecutor
36 * has a parent executor which is given at construction time (defaults to
37 * folly's global CPUExecutor). Tasks added to SerialExecutor are executed
38 * in the parent executor, however strictly non-concurrently and in the order
39 * they were added.
40 *
41 * SerialExecutor tries to schedule its tasks fairly. Every task submitted to
42 * it results in one task submitted to the parent executor. Whenever the parent
43 * executor executes one of those, one of the tasks submitted to SerialExecutor
44 * is marked for execution, which means it will either be executed at once,
45 * or if a task is currently being executed already, after that.
46 *
47 * The SerialExecutor may be deleted at any time. All tasks that have been
48 * submitted will still be executed with the same guarantees, as long as the
49 * parent executor is executing tasks.
50 */
51
52class SerialExecutor : public SequencedExecutor {
53 public:
54 SerialExecutor(SerialExecutor const&) = delete;
55 SerialExecutor& operator=(SerialExecutor const&) = delete;
56 SerialExecutor(SerialExecutor&&) = delete;
57 SerialExecutor& operator=(SerialExecutor&&) = delete;
58
59 static KeepAlive<SerialExecutor> create(
60 KeepAlive<Executor> parent = getKeepAliveToken(getCPUExecutor().get()));
61
62 class Deleter {
63 public:
64 Deleter() {}
65
66 void operator()(SerialExecutor* executor) {
67 executor->keepAliveRelease();
68 }
69
70 private:
71 friend class SerialExecutor;
72 explicit Deleter(std::shared_ptr<Executor> parent)
73 : parent_(std::move(parent)) {}
74
75 std::shared_ptr<Executor> parent_;
76 };
77
78 using UniquePtr = std::unique_ptr<SerialExecutor, Deleter>;
79 [[deprecated("Replaced by create")]] static UniquePtr createUnique(
80 std::shared_ptr<Executor> parent = getCPUExecutor());
81
82 /**
83 * Add one task for execution in the parent executor
84 */
85 void add(Func func) override;
86
87 /**
88 * Add one task for execution in the parent executor, and use the given
89 * priority for one task submission to parent executor.
90 *
91 * Since in-order execution of tasks submitted to SerialExecutor is
92 * guaranteed, the priority given here does not necessarily reflect the
93 * execution priority of the task submitted with this call to
94 * `addWithPriority`. The given priority is passed on to the parent executor
95 * for the execution of one of the SerialExecutor's tasks.
96 */
97 void addWithPriority(Func func, int8_t priority) override;
98 uint8_t getNumPriorities() const override {
99 return parent_->getNumPriorities();
100 }
101
102 protected:
103 bool keepAliveAcquire() override;
104
105 void keepAliveRelease() override;
106
107 private:
108 explicit SerialExecutor(KeepAlive<Executor> parent);
109 ~SerialExecutor() override;
110
111 void run();
112
113 KeepAlive<Executor> parent_;
114 std::atomic<std::size_t> scheduled_{0};
115 /**
116 * Unbounded multi producer single consumer queue where consumers don't block
117 * on dequeue.
118 */
119 folly::UnboundedQueue<Func, false, true, false> queue_;
120
121 std::atomic<ssize_t> keepAliveCounter_{1};
122};
123
124} // namespace folly
125