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 <folly/CppAttributes.h>
20 #include <folly/Function.h>
21 
22 #include <atomic>
23 #include <memory>
24 #include <thread>
25 #include <type_traits>
26 
27 namespace folly {
28 
29 class CancellationCallback;
30 class CancellationSource;
31 struct OperationCancelled : public std::exception {
whatOperationCancelled32   const char* what() const noexcept override {
33     return "coroutine operation cancelled";
34   }
35 };
36 
37 namespace detail {
38 class CancellationState;
39 struct CancellationStateTokenDeleter {
40   void operator()(CancellationState*) noexcept;
41 };
42 struct CancellationStateSourceDeleter {
43   void operator()(CancellationState*) noexcept;
44 };
45 using CancellationStateTokenPtr =
46     std::unique_ptr<CancellationState, CancellationStateTokenDeleter>;
47 using CancellationStateSourcePtr =
48     std::unique_ptr<CancellationState, CancellationStateSourceDeleter>;
49 template <typename...>
50 struct WithDataTag;
51 } // namespace detail
52 
53 // A CancellationToken is an object that can be passed into an function or
54 // operation that allows the caller to later request that the operation be
55 // cancelled.
56 //
57 // A CancellationToken object can be obtained by calling the .getToken()
58 // method on a CancellationSource or by copying another CancellationToken
59 // object. All CancellationToken objects obtained from the same original
60 // CancellationSource object all reference the same underlying cancellation
61 // state and will all be cancelled together.
62 //
63 // If your function needs to be cancellable but does not need to request
64 // cancellation then you should take a CancellationToken as a parameter.
65 // If your function needs to be able to request cancellation then you
66 // should instead take a CancellationSource as a parameter.
67 class CancellationToken {
68  public:
69   // Constructs to a token that can never be cancelled.
70   //
71   // Pass a default-constructed CancellationToken into an operation that
72   // you never intend to cancel. These objects are very cheap to create.
73   CancellationToken() noexcept = default;
74 
75   // Construct a copy of the token that shares the same underlying state.
76   CancellationToken(const CancellationToken& other) noexcept;
77   CancellationToken(CancellationToken&& other) noexcept;
78 
79   CancellationToken& operator=(const CancellationToken& other) noexcept;
80   CancellationToken& operator=(CancellationToken&& other) noexcept;
81 
82   // Query whether someone has called .requestCancellation() on an instance
83   // of CancellationSource object associated with this CancellationToken.
84   bool isCancellationRequested() const noexcept;
85 
86   // Query whether this CancellationToken can ever have cancellation requested
87   // on it.
88   //
89   // This will return false if the CancellationToken is not associated with a
90   // CancellationSource object. eg. because the CancellationToken was
91   // default-constructed, has been moved-from or because the last
92   // CancellationSource object associated with the underlying cancellation state
93   // has been destroyed and the operation has not yet been cancelled and so
94   // never will be.
95   //
96   // Implementations of operations may be able to take more efficient code-paths
97   // if they know they can never be cancelled.
98   bool canBeCancelled() const noexcept;
99 
100   // Obtain a CancellationToken linked to any number of other
101   // CancellationTokens.
102   //
103   // This token will have cancellation requested when any of the passed-in
104   // tokens do.
105   // This token is cancellable if any of the passed-in tokens are at the time of
106   // construction.
107   template <typename... Ts>
108   static CancellationToken merge(Ts&&... tokens);
109 
110   void swap(CancellationToken& other) noexcept;
111 
112   friend bool operator==(
113       const CancellationToken& a, const CancellationToken& b) noexcept;
114 
115  private:
116   friend class CancellationCallback;
117   friend class CancellationSource;
118 
119   explicit CancellationToken(detail::CancellationStateTokenPtr state) noexcept;
120 
121   detail::CancellationStateTokenPtr state_;
122 };
123 
124 bool operator==(
125     const CancellationToken& a, const CancellationToken& b) noexcept;
126 bool operator!=(
127     const CancellationToken& a, const CancellationToken& b) noexcept;
128 
129 // A CancellationSource object provides the ability to request cancellation of
130 // operations that an associated CancellationToken was passed to.
131 //
132 // Example usage:
133 //   CancellationSource cs;
134 //   Future<void> f = startSomeOperation(cs.getToken());
135 //
136 //   // Later...
137 //   cs.requestCancellation();
138 class CancellationSource {
139  public:
140   // Construct to a new, independent cancellation source.
141   CancellationSource();
142 
143   // Construct a new reference to the same underlying cancellation state.
144   //
145   // Either the original or the new copy can be used to request cancellation
146   // of associated work.
147   CancellationSource(const CancellationSource& other) noexcept;
148 
149   // This leaves 'other' in an empty state where 'requestCancellation()' is a
150   // no-op and 'canBeCancelled()' returns false.
151   CancellationSource(CancellationSource&& other) noexcept;
152 
153   CancellationSource& operator=(const CancellationSource& other) noexcept;
154   CancellationSource& operator=(CancellationSource&& other) noexcept;
155 
156   // Construct a CancellationSource that cannot be cancelled.
157   //
158   // This factory function can be used to obtain a CancellationSource that
159   // is equivalent to a moved-from CancellationSource object without needing
160   // to allocate any shared-state.
161   static CancellationSource invalid() noexcept;
162 
163   // Query if cancellation has already been requested on this CancellationSource
164   // or any other CancellationSource object copied from the same original
165   // CancellationSource object.
166   bool isCancellationRequested() const noexcept;
167 
168   // Query if cancellation can be requested through this CancellationSource
169   // object. This will only return false if the CancellationSource object has
170   // been moved-from.
171   bool canBeCancelled() const noexcept;
172 
173   // Obtain a CancellationToken linked to this CancellationSource.
174   //
175   // This token can be passed into cancellable operations to allow the caller
176   // to later request cancellation of that operation.
177   CancellationToken getToken() const noexcept;
178 
179   // Request cancellation of work associated with this CancellationSource.
180   //
181   // This will ensure subsequent calls to isCancellationRequested() on any
182   // CancellationSource or CancellationToken object associated with the same
183   // underlying cancellation state to return true.
184   //
185   // If this is the first call to requestCancellation() on any
186   // CancellationSource object with the same underlying state then this call
187   // will also execute the callbacks associated with any CancellationCallback
188   // objects that were constructed with an associated CancellationToken.
189   //
190   // Note that it is possible that another thread may be concurrently
191   // registering a callback with CancellationCallback. This method guarantees
192   // that either this thread will see the callback registration and will
193   // ensure that the callback is called, or the CancellationCallback constructor
194   // will see the cancellation-requested signal and will execute the callback
195   // inline inside the constructor.
196   //
197   // Returns the previous state of 'isCancellationRequested()'. i.e.
198   // - 'true' if cancellation had previously been requested.
199   // - 'false' if this was the first call to request cancellation.
200   bool requestCancellation() const noexcept;
201 
202   void swap(CancellationSource& other) noexcept;
203 
204   friend bool operator==(
205       const CancellationSource& a, const CancellationSource& b) noexcept;
206 
207   template <typename... Data, typename... Args>
208   static std::pair<CancellationSource, std::tuple<Data...>*> create(
209       detail::WithDataTag<Data...>, Args&&...);
210 
211  private:
212   explicit CancellationSource(
213       detail::CancellationStateSourcePtr&& state) noexcept;
214 
215   detail::CancellationStateSourcePtr state_;
216 };
217 
218 bool operator==(
219     const CancellationSource& a, const CancellationSource& b) noexcept;
220 bool operator!=(
221     const CancellationSource& a, const CancellationSource& b) noexcept;
222 
223 class CancellationCallback {
224   using VoidFunction = folly::Function<void()>;
225 
226  public:
227   // Constructing a CancellationCallback object registers the callback
228   // with the specified CancellationToken such that the callback will be
229   // executed if the corresponding CancellationSource object has the
230   // requestCancellation() method called on it.
231   //
232   // If the CancellationToken object already had cancellation requested
233   // then the callback will be executed inline on the current thread before
234   // the constructor returns. Otherwise, the callback will be executed on
235   // in the execution context of the first thread to call requestCancellation()
236   // on a corresponding CancellationSource.
237   //
238   // The callback object must not throw any unhandled exceptions. Doing so
239   // will result in the program terminating via std::terminate().
240   template <
241       typename Callable,
242       std::enable_if_t<
243           std::is_constructible<VoidFunction, Callable>::value,
244           int> = 0>
245   CancellationCallback(CancellationToken&& ct, Callable&& callable);
246   template <
247       typename Callable,
248       std::enable_if_t<
249           std::is_constructible<VoidFunction, Callable>::value,
250           int> = 0>
251   CancellationCallback(const CancellationToken& ct, Callable&& callable);
252 
253   // Deregisters the callback from the CancellationToken.
254   //
255   // If cancellation has been requested concurrently on another thread and the
256   // callback is currently executing then the destructor will block until after
257   // the callback has returned (otherwise it might be left with a dangling
258   // reference).
259   //
260   // You should generally try to implement your callback functions to be lock
261   // free to avoid deadlocks between the callback executing and the
262   // CancellationCallback destructor trying to deregister the callback.
263   //
264   // If the callback has not started executing yet then the callback will be
265   // deregistered from the CancellationToken before the destructor completes.
266   //
267   // Once the destructor returns you can be guaranteed that the callback will
268   // not be called by a subsequent call to 'requestCancellation()' on a
269   // CancellationSource associated with the CancellationToken passed to the
270   // constructor.
271   ~CancellationCallback();
272 
273   // Not copyable/movable
274   CancellationCallback(const CancellationCallback&) = delete;
275   CancellationCallback(CancellationCallback&&) = delete;
276   CancellationCallback& operator=(const CancellationCallback&) = delete;
277   CancellationCallback& operator=(CancellationCallback&&) = delete;
278 
279  private:
280   friend class detail::CancellationState;
281 
282   void invokeCallback() noexcept;
283 
284   CancellationCallback* next_;
285 
286   // Pointer to the pointer that points to this node in the linked list.
287   // This could be the 'next_' of a previous CancellationCallback or could
288   // be the 'head_' pointer of the CancellationState.
289   // If this node is inserted in the list then this will be non-null.
290   CancellationCallback** prevNext_;
291 
292   detail::CancellationState* state_;
293   VoidFunction callback_;
294 
295   // Pointer to a flag stored on the stack of the caller to invokeCallback()
296   // that is used to indicate to the caller of invokeCallback() that the
297   // destructor has run and it is no longer valid to access the callback
298   // object.
299   bool* destructorHasRunInsideCallback_;
300 
301   // Flag used to signal that the callback has completed executing on another
302   // thread and it is now safe to exit the destructor.
303   std::atomic<bool> callbackCompleted_;
304 };
305 
306 } // namespace folly
307 
308 #include <folly/CancellationToken-inl.h>
309