1 // Copyright 2018 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIB_FIT_SCOPE_H_ 6 #define LIB_FIT_SCOPE_H_ 7 8 #include <assert.h> 9 10 #include <atomic> 11 #include <mutex> 12 13 #include "promise.h" 14 #include "thread_safety.h" 15 16 namespace fit { 17 18 // Provides a mechanism for binding promises to the lifetime of another object 19 // such that they are destroyed before that object goes out of scope. It is 20 // particularly useful for ensuring that the lifetime of a promise does not 21 // exceed the lifetime of any variables that it has captured by reference. 22 // 23 // A scope is thread-safe but non-reentrant: it must not be destroyed while 24 // any of its associated promises are running. 25 // 26 // EXAMPLE 27 // 28 // Define a |fit::scope| as a member of the object to whose lifetime the 29 // promises should be bound. 30 // 31 // // We mark this class final because its destructor has side-effects 32 // // that rely on the order of destruction. If this object were 33 // // subclassed there would be a possibility for promises bound to its 34 // // scope to inadvertently access the subclass's state while the object 35 // // was being destroyed. 36 // class accumulator final { 37 // public: 38 // accumulator() = default; 39 // ~accumulator() = default; 40 // 41 // fit::promise<int> accumulate(int value); 42 // 43 // private: 44 // int prior_total_ = 0; 45 // 46 // // This member is last so that the scope is exited before all 47 // // other members of the object are destroyed. Alternately, we 48 // // could enforce this ordering by explicitly invoking 49 // // |fit::scope::exit()| where appropriate. 50 // fit::scope scope_; 51 // }; 52 // 53 // Use |fit::promise::wrap_with()| to wrap up promises that capture pointers 54 // to the object. In this example, the captured pointer is "this". 55 // 56 // fit::promise<int> accumulator::accumulate(int value) { 57 // return fit::make_promise([this, value] { 58 // prior_total_ += value; 59 // return fit::ok(prior_total_); 60 // }).wrap_with(scope_); /* binding to scope happens here */ 61 // } 62 // 63 class scope final { 64 public: 65 // Creates a new scope. 66 scope(); 67 68 // Exits the scope and destroys all of its wrapped promises. 69 // Asserts that no promises are currently running. 70 ~scope(); 71 72 // Returns true if the scope has been exited. 73 // 74 // This method is thread-safe. exited()75 bool exited() const { return state_->exited(); } 76 77 // Exits the scope and destroys all of its wrapped promises. 78 // Assets that no promises are currently running. 79 // 80 // This method is thread-safe. exit()81 void exit() { return state_->exit(false /*scope_was_destroyed*/); } 82 83 // Returns a promise which wraps the specified |promise| and binds the 84 // promise to this scope. 85 // 86 // The specified promise will automatically be destroyed when its wrapper 87 // is destroyed or when the scope is exited. If the scope has already 88 // exited then the wrapped promise will be immediately destroyed. 89 // 90 // When the returned promise is invoked before the scope is exited, 91 // the promise that it wraps will be invoked as usual. However, when 92 // the returned promise is invoked after the scope is exited, it 93 // immediately returns a pending result (since the promise that it 94 // previously wrapped has already been destroyed). By returning a 95 // pending result, the return promise effectively indicates to the 96 // executor that the task has been "abandoned" due to the scope being 97 // exited. 98 // 99 // This method is thread-safe. 100 template <typename Promise> decltype(auto)101 decltype(auto) wrap(Promise promise) { 102 assert(promise); 103 return fit::make_promise_with_continuation( 104 scoped_continuation<Promise>( 105 state_->adopt_promise( 106 new promise_holder<Promise>(std::move(promise))))); 107 } 108 109 scope(const scope&) = delete; 110 scope(scope&&) = delete; 111 scope& operator=(const scope&) = delete; 112 scope& operator=(scope&&) = delete; 113 114 private: 115 class state; 116 class promise_holder_base; 117 118 // Holds a reference to a promise that is owned by the state. 119 class promise_handle final { 120 public: 121 promise_handle() = default; 122 123 private: 124 // |state| and |promise_holder| belong to the state object. 125 // Invariant: If |promise_holder| is non-null then |state| is 126 // also non-null. 127 friend state; promise_handle(state * state,promise_holder_base * promise_holder)128 promise_handle(state* state, promise_holder_base* promise_holder) 129 : state(state), promise_holder(promise_holder) {} 130 131 state* state = nullptr; 132 promise_holder_base* promise_holder = nullptr; 133 }; 134 135 // Holds the shared state of the scope. 136 // This object is destroyed once the scope and all of its promises 137 // have been destroyed. 138 class state final { 139 public: 140 state(); 141 ~state(); 142 143 // The following methods are called from the |scope|. 144 145 bool exited() const; 146 void exit(bool scope_was_destroyed); 147 148 // The following methods are called from the |scoped_continuation|. 149 150 // Links a promise to the scope's lifecycle such that it will be 151 // destroyed when the scope is exited. Returns a handle that may 152 // be used to access the promise later. 153 // The state takes ownership of the promise. 154 promise_handle adopt_promise(promise_holder_base* promise_holder); 155 156 // Unlinks a promise from the scope's lifecycle given its handle 157 // and causes the underlying promise to be destroyed if it hasn't 158 // already been destroyed due to the scope exiting. 159 // Does nothing if the handle was default-initialized. 160 static void drop_promise(promise_handle promise_handle); 161 162 // Acquires a promise given its handle. 163 // Returns nullptr if the handle was default-initialized or if 164 // the scope exited, meaning that the promise was not acquired. 165 // The promise must be released before it can be acquired again. 166 static promise_holder_base* try_acquire_promise( 167 promise_handle promise_handle); 168 169 // Releases a promise that was successfully acquired. 170 static void release_promise(promise_handle promise_handle); 171 172 state(const state&) = delete; 173 state(state&&) = delete; 174 state& operator=(const state&) = delete; 175 state& operator=(state&&) = delete; 176 177 private: should_delete_self()178 bool should_delete_self() const FIT_REQUIRES(mutex_) { 179 return scope_was_destroyed_ && promise_handle_count_ == 0; 180 } 181 182 static constexpr uint64_t scope_exited = static_cast<uint64_t>(1u) << 63; 183 184 // Tracks of the number of promises currently running ("acquired"). 185 // The top bit is set when the scope is exited, at which point no 186 // new promises can be acquired. After exiting, the count can 187 // be incremented transiently but is immediately decremented again 188 // until all promise handles have been released. Once no promise 189 // handles remain, the count will equal |scope_exited| and will not 190 // change again. 191 std::atomic_uint64_t acquired_promise_count_{0}; 192 193 mutable std::mutex mutex_; 194 bool scope_was_destroyed_ FIT_GUARDED(mutex_) = false; 195 uint64_t promise_handle_count_ FIT_GUARDED(mutex_) = 0; 196 promise_holder_base* head_promise_holder_ FIT_GUARDED(mutex_) = nullptr; 197 }; 198 199 // Base type for managing the lifetime of a promise of any type. 200 // It is owned by the state and retained indirectly by the continuation 201 // using a |promise_handle|. 202 class promise_holder_base { 203 public: 204 promise_holder_base() = default; 205 virtual ~promise_holder_base() = default; 206 207 promise_holder_base(const promise_holder_base&) = delete; 208 promise_holder_base(promise_holder_base&&) = delete; 209 promise_holder_base& operator=(const promise_holder_base&) = delete; 210 promise_holder_base& operator=(promise_holder_base&&) = delete; 211 212 private: 213 // |next| and |prev| belong to the state object. 214 friend class state; 215 promise_holder_base* next = nullptr; 216 promise_holder_base* prev = nullptr; 217 }; 218 219 // Holder for a promise of a particular type. 220 template <typename Promise> 221 class promise_holder final : public promise_holder_base { 222 public: promise_holder(Promise promise)223 explicit promise_holder(Promise promise) 224 : promise(std::move(promise)) {} 225 ~promise_holder() override = default; 226 227 Promise promise; 228 }; 229 230 // Wraps a promise whose lifetime is managed by the scope. 231 template <typename Promise> 232 class scoped_continuation final { 233 public: scoped_continuation(promise_handle promise_handle)234 explicit scoped_continuation(promise_handle promise_handle) 235 : promise_handle_(promise_handle) {} 236 scoped_continuation(scoped_continuation && other)237 scoped_continuation(scoped_continuation&& other) 238 : promise_handle_(other.promise_handle_) { 239 other.promise_handle_ = promise_handle{}; 240 } 241 ~scoped_continuation()242 ~scoped_continuation() { 243 state::drop_promise(promise_handle_); 244 } 245 operator()246 typename Promise::result_type operator()(context& context) { 247 typename Promise::result_type result; 248 auto holder = static_cast<promise_holder<Promise>*>( 249 state::try_acquire_promise(promise_handle_)); 250 if (holder) { 251 result = holder->promise(context); 252 state::release_promise(promise_handle_); 253 } 254 return result; 255 } 256 257 scoped_continuation& operator=(scoped_continuation&& other) { 258 if (this != &other) { 259 state::drop_promise(promise_handle_); 260 promise_handle_ = other.promise_handle_; 261 other.promise_handle_ = promise_handle{}; 262 } 263 return *this; 264 } 265 266 scoped_continuation(const scoped_continuation&) = delete; 267 scoped_continuation& operator=(const scoped_continuation&) = delete; 268 269 private: 270 promise_handle promise_handle_; 271 }; 272 273 // The scope's shared state. 274 state* const state_; 275 }; 276 277 } // namespace fit 278 279 #endif // LIB_FIT_SCOPE_H_ 280