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