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