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