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_SEQUENCER_H_
6 #define LIB_FIT_SEQUENCER_H_
7 
8 #include <assert.h>
9 
10 #include <mutex>
11 
12 #include "bridge.h"
13 #include "thread_safety.h"
14 
15 namespace fit {
16 
17 // A sequencer imposes a first-in-first-out sequential execution order onto a
18 // sequence of promises.  Each successively enqueued promise remains suspended
19 // until all previously enqueued promises complete or are abandoned.
20 //
21 // |fit::sequencer| is designed to be used either on its own or chained
22 // onto a promise using |fit::promise::wrap_with()|.
23 //
24 // EXAMPLE
25 //
26 //     // This wrapper type is intended to be applied to
27 //     // a sequence of promises so we store it in a variable.
28 //     fit::sequencer seq;
29 //
30 //     // This task consists of some amount of work that must be
31 //     // completed sequentially followed by other work that can
32 //     // happen in any order.  We use |wrap_with()| to wrap the
33 //     // sequential work with the sequencer.
34 //     fit::promise<> perform_complex_task() {
35 //         return fit::make_promise([] { /* do sequential work */ })
36 //             .then([] (fit::result<> result) { /* this will also be wrapped */ })
37 //             .wrap_with(seq)
38 //             .then([] (fit::result<> result) { /* do more work */ });
39 //     }
40 //
41 class sequencer final {
42 public:
43     sequencer();
44     ~sequencer();
45 
46     // Returns a new promise which will invoke |promise| after all previously
47     // enqueued promises on this sequencer have completed or been abandoned.
48     //
49     // This method is thread-safe.
50     template <typename Promise>
decltype(auto)51     decltype(auto) wrap(Promise promise) {
52         assert(promise);
53 
54         fit::bridge<> bridge;
55         fit::consumer<> prior = swap_prior(std::move(bridge.consumer));
56         return prior.promise_or(fit::ok())
57             .then([promise = std::move(promise),
58                    completer = std::move(bridge.completer)](
59                       fit::context& context, fit::result<>) mutable {
60                 // This handler will run once the completer associated
61                 // with the |prior| promise is abandoned.  Once the promise
62                 // has finished, both the promise and completer will be
63                 // destroyed thereby causing the next promise chained onto
64                 // the |bridge|'s associated consumer to become runnable.
65                 return promise(context);
66             });
67     }
68 
69     sequencer(const sequencer&) = delete;
70     sequencer(sequencer&&) = delete;
71     sequencer& operator=(const sequencer&) = delete;
72     sequencer& operator=(sequencer&&) = delete;
73 
74 private:
75     fit::consumer<> swap_prior(fit::consumer<> new_prior);
76 
77     std::mutex mutex_;
78 
79     // Holds the consumption capability of the most recently wrapped promise.
80     fit::consumer<> prior_ FIT_GUARDED(mutex_);
81 };
82 
83 } // namespace fit
84 
85 #endif // LIB_FIT_SEQUENCER_H_
86