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 | |
26 | namespace folly { |
27 | namespace pushmi { |
28 | // derive from this for types that need to find operator|() overloads by ADL |
29 | struct folly_pipeorigin {}; |
30 | } // namespace pushmi |
31 | |
32 | using Func = Function<void()>; |
33 | |
34 | namespace detail { |
35 | |
36 | class 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. |
55 | class 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. |
278 | template <typename ExecutorT> |
279 | Executor::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 | |
286 | template <typename ExecutorT> |
287 | Executor::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 | |
294 | template <typename ExecutorT> |
295 | Executor::KeepAlive<ExecutorT> getKeepAliveToken( |
296 | Executor::KeepAlive<ExecutorT>& ka) { |
297 | return ka.copy(); |
298 | } |
299 | |
300 | } // namespace folly |
301 | |