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