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