1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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 <cassert>
20#include <climits>
21#include <utility>
22
23#include <folly/Function.h>
24#include <folly/Utility.h>
25
26namespace folly {
27namespace pushmi {
28// derive from this for types that need to find operator|() overloads by ADL
29struct folly_pipeorigin {};
30} // namespace pushmi
31
32using Func = Function<void()>;
33
34namespace detail {
35
36class ExecutorKeepAliveBase {
37 public:
38 // A dummy keep-alive is a keep-alive to an executor which does not support
39 // the keep-alive mechanism.
40 static constexpr uintptr_t kDummyFlag = uintptr_t(1) << 0;
41
42 // An alias keep-alive is a keep-alive to an executor to which there is
43 // known to be another keep-alive whose lifetime surrounds the lifetime of
44 // the alias.
45 static constexpr uintptr_t kAliasFlag = uintptr_t(1) << 1;
46
47 static constexpr uintptr_t kFlagMask = kDummyFlag | kAliasFlag;
48 static constexpr uintptr_t kExecutorMask = ~kFlagMask;
49};
50
51} // namespace detail
52
53/// An Executor accepts units of work with add(), which should be
54/// threadsafe.
55class Executor {
56 public:
57 // Workaround for a linkage problem with explicitly defaulted dtor t22914621
58 virtual ~Executor() {}
59
60 /// Enqueue a function to executed by this executor. This and all
61 /// variants must be threadsafe.
62 virtual void add(Func) = 0;
63
64 /// Enqueue a function with a given priority, where 0 is the medium priority
65 /// This is up to the implementation to enforce
66 virtual void addWithPriority(Func, int8_t priority);
67
68 virtual uint8_t getNumPriorities() const {
69 return 1;
70 }
71
72 static const int8_t LO_PRI = SCHAR_MIN;
73 static const int8_t MID_PRI = 0;
74 static const int8_t HI_PRI = SCHAR_MAX;
75
76 /**
77 * Executor::KeepAlive is a safe pointer to an Executor.
78 * For any Executor that supports KeepAlive functionality, Executor's
79 * destructor will block until all the KeepAlive objects associated with that
80 * Executor are destroyed.
81 * For Executors that don't support the KeepAlive funcionality, KeepAlive
82 * doesn't provide such protection.
83 *
84 * KeepAlive should *always* be used instead of Executor*. KeepAlive can be
85 * implicitly constructed from Executor*. getKeepAliveToken() helper method
86 * can be used to construct a KeepAlive in templated code if you need to
87 * preserve the original Executor type.
88 */
89 template <typename ExecutorT = Executor>
90 class KeepAlive : pushmi::folly_pipeorigin,
91 private detail::ExecutorKeepAliveBase {
92 public:
93 using KeepAliveFunc = Function<void(KeepAlive&&)>;
94
95 KeepAlive() = default;
96
97 ~KeepAlive() {
98 reset();
99 }
100
101 KeepAlive(KeepAlive&& other) noexcept
102 : storage_(std::exchange(other.storage_, 0)) {}
103
104 KeepAlive(const KeepAlive& other) noexcept
105 : KeepAlive(getKeepAliveToken(other.get())) {}
106
107 template <
108 typename OtherExecutor,
109 typename = typename std::enable_if<
110 std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
111 /* implicit */ KeepAlive(KeepAlive<OtherExecutor>&& other) noexcept
112 : KeepAlive(other.get(), other.storage_ & kFlagMask) {
113 other.storage_ = 0;
114 }
115
116 template <
117 typename OtherExecutor,
118 typename = typename std::enable_if<
119 std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
120 /* implicit */ KeepAlive(const KeepAlive<OtherExecutor>& other) noexcept
121 : KeepAlive(getKeepAliveToken(other.get())) {}
122
123 /* implicit */ KeepAlive(ExecutorT* executor) {
124 *this = getKeepAliveToken(executor);
125 }
126
127 KeepAlive& operator=(KeepAlive&& other) {
128 reset();
129 storage_ = std::exchange(other.storage_, 0);
130 return *this;
131 }
132
133 KeepAlive& operator=(KeepAlive const& other) {
134 return operator=(folly::copy(other));
135 }
136
137 template <
138 typename OtherExecutor,
139 typename = typename std::enable_if<
140 std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
141 KeepAlive& operator=(KeepAlive<OtherExecutor>&& other) {
142 return *this = KeepAlive(std::move(other));
143 }
144
145 template <
146 typename OtherExecutor,
147 typename = typename std::enable_if<
148 std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>
149 KeepAlive& operator=(const KeepAlive<OtherExecutor>& other) {
150 return *this = KeepAlive(other);
151 }
152
153 void reset() {
154 if (Executor* executor = get()) {
155 auto const flags = std::exchange(storage_, 0) & kFlagMask;
156 if (!(flags & (kDummyFlag | kAliasFlag))) {
157 executor->keepAliveRelease();
158 }
159 }
160 }
161
162 explicit operator bool() const {
163 return storage_;
164 }
165
166 ExecutorT* get() const {
167 return reinterpret_cast<ExecutorT*>(storage_ & kExecutorMask);
168 }
169
170 ExecutorT& operator*() const {
171 return *get();
172 }
173
174 ExecutorT* operator->() const {
175 return get();
176 }
177
178 KeepAlive copy() const {
179 return isKeepAliveDummy(*this) //
180 ? makeKeepAliveDummy(get())
181 : getKeepAliveToken(get());
182 }
183
184 KeepAlive get_alias() const {
185 return KeepAlive(storage_ | kAliasFlag);
186 }
187
188 template <class KAF>
189 void add(KAF&& f) && {
190 static_assert(
191 is_invocable<KAF, KeepAlive&&>::value,
192 "Parameter to add must be void(KeepAlive&&)>");
193 auto ex = get();
194 ex->add([ka = std::move(*this), f = std::forward<KAF>(f)]() mutable {
195 f(std::move(ka));
196 });
197 }
198
199 private:
200 friend class Executor;
201 template <typename OtherExecutor>
202 friend class KeepAlive;
203
204 KeepAlive(ExecutorT* executor, uintptr_t flags) noexcept
205 : storage_(reinterpret_cast<uintptr_t>(executor) | flags) {
206 assert(executor);
207 assert(!(reinterpret_cast<uintptr_t>(executor) & ~kExecutorMask));
208 assert(!(flags & kExecutorMask));
209 }
210
211 explicit KeepAlive(uintptr_t storage) noexcept : storage_(storage) {}
212
213 // Combined storage for the executor pointer and for all flags.
214 uintptr_t storage_{reinterpret_cast<uintptr_t>(nullptr)};
215 };
216
217 template <typename ExecutorT>
218 static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {
219 static_assert(
220 std::is_base_of<Executor, ExecutorT>::value,
221 "getKeepAliveToken only works for folly::Executor implementations.");
222 if (!executor) {
223 return {};
224 }
225 folly::Executor* executorPtr = executor;
226 if (executorPtr->keepAliveAcquire()) {
227 return makeKeepAlive<ExecutorT>(executor);
228 }
229 return makeKeepAliveDummy<ExecutorT>(executor);
230 }
231
232 template <typename ExecutorT>
233 static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) {
234 static_assert(
235 std::is_base_of<Executor, ExecutorT>::value,
236 "getKeepAliveToken only works for folly::Executor implementations.");
237 return getKeepAliveToken(&executor);
238 }
239
240 protected:
241 /**
242 * Returns true if the KeepAlive is constructed from an executor that does
243 * not support the keep alive ref-counting functionality
244 */
245 template <typename ExecutorT>
246 static bool isKeepAliveDummy(const KeepAlive<ExecutorT>& keepAlive) {
247 return keepAlive.storage_ & KeepAlive<ExecutorT>::kDummyFlag;
248 }
249
250 // Acquire a keep alive token. Should return false if keep-alive mechanism
251 // is not supported.
252 virtual bool keepAliveAcquire();
253 // Release a keep alive token previously acquired by keepAliveAcquire().
254 // Will never be called if keepAliveAcquire() returns false.
255 virtual void keepAliveRelease();
256
257 template <typename ExecutorT>
258 static KeepAlive<ExecutorT> makeKeepAlive(ExecutorT* executor) {
259 static_assert(
260 std::is_base_of<Executor, ExecutorT>::value,
261 "makeKeepAlive only works for folly::Executor implementations.");
262 return KeepAlive<ExecutorT>{executor, uintptr_t(0)};
263 }
264
265 private:
266 template <typename ExecutorT>
267 static KeepAlive<ExecutorT> makeKeepAliveDummy(ExecutorT* executor) {
268 static_assert(
269 std::is_base_of<Executor, ExecutorT>::value,
270 "makeKeepAliveDummy only works for folly::Executor implementations.");
271 return KeepAlive<ExecutorT>{executor, KeepAlive<ExecutorT>::kDummyFlag};
272 }
273};
274
275/// Returns a keep-alive token which guarantees that Executor will keep
276/// processing tasks until the token is released (if supported by Executor).
277/// KeepAlive always contains a valid pointer to an Executor.
278template <typename ExecutorT>
279Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {
280 static_assert(
281 std::is_base_of<Executor, ExecutorT>::value,
282 "getKeepAliveToken only works for folly::Executor implementations.");
283 return Executor::getKeepAliveToken(executor);
284}
285
286template <typename ExecutorT>
287Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) {
288 static_assert(
289 std::is_base_of<Executor, ExecutorT>::value,
290 "getKeepAliveToken only works for folly::Executor implementations.");
291 return getKeepAliveToken(&executor);
292}
293
294template <typename ExecutorT>
295Executor::KeepAlive<ExecutorT> getKeepAliveToken(
296 Executor::KeepAlive<ExecutorT>& ka) {
297 return ka.copy();
298}
299
300} // namespace folly
301