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_DEFER_H_
6 #define LIB_FIT_DEFER_H_
7
8 #include <utility>
9
10 #include "function.h"
11 #include "nullable.h"
12
13 namespace fit {
14
15 // A move-only deferred action wrapper with RAII semantics.
16 // This class is not thread safe.
17 //
18 // The wrapper holds a function-like callable target with no arguments
19 // which it invokes when it goes out of scope unless canceled, called, or
20 // moved to a wrapper in a different scope.
21 //
22 // See |fit::defer()| for idiomatic usage.
23 template <typename T>
24 class deferred_action final {
25 public:
26 // Creates a deferred action without a pending target.
27 deferred_action() = default;
deferred_action(decltype (nullptr))28 explicit deferred_action(decltype(nullptr)) {}
29
30 // Creates a deferred action with a pending target.
deferred_action(T target)31 explicit deferred_action(T target) : target_(std::move(target)) {}
32
33 // Creates a deferred action with a pending target moved from another
34 // deferred action, leaving the other one without a pending target.
deferred_action(deferred_action && other)35 deferred_action(deferred_action&& other) : target_(std::move(other.target_)) {
36 other.target_.reset();
37 }
38
39 // Invokes and releases the deferred action's pending target (if any).
~deferred_action()40 ~deferred_action() { call(); }
41
42 // Returns true if the deferred action has a pending target.
43 explicit operator bool() const { return !!target_; }
44
45 // Invokes and releases the deferred action's pending target (if any),
46 // then move-assigns it from another deferred action, leaving the latter
47 // one without a pending target.
48 deferred_action& operator=(deferred_action&& other) {
49 if (&other == this)
50 return *this;
51 call();
52 target_ = std::move(other.target_);
53 other.target_.reset();
54 return *this;
55 }
56
57 // Invokes and releases the deferred action's pending target (if any).
call()58 void call() {
59 if (target_) {
60 // Move to a local to guard against re-entrance.
61 T local_target = std::move(*target_);
62 target_.reset();
63 local_target();
64 }
65 }
66
67 // Releases the deferred action's pending target (if any) without
68 // invoking it.
cancel()69 void cancel() { target_.reset(); }
decltype(nullptr)70 deferred_action& operator=(decltype(nullptr)) {
71 cancel();
72 return *this;
73 }
74
75 // Assigns a new target to the deferred action.
76 deferred_action& operator=(T target) {
77 target_ = std::move(target);
78 return *this;
79 }
80
81 deferred_action(const deferred_action& other) = delete;
82 deferred_action& operator=(const deferred_action& other) = delete;
83
84 private:
85 nullable<T> target_;
86 };
87
88 template <typename T>
89 bool operator==(const deferred_action<T>& action, decltype(nullptr)) {
90 return !action;
91 }
92 template <typename T>
93 bool operator==(decltype(nullptr), const deferred_action<T>& action) {
94 return !action;
95 }
96 template <typename T>
97 bool operator!=(const deferred_action<T>& action, decltype(nullptr)) {
98 return !!action;
99 }
100 template <typename T>
101 bool operator!=(decltype(nullptr), const deferred_action<T>& action) {
102 return !!action;
103 }
104
105 // Defers execution of a function-like callable target with no arguments
106 // until the value returned by this function goes out of scope unless canceled,
107 // called, or moved to a wrapper in a different scope.
108 //
109 // // This example prints "Hello..." then "Goodbye!".
110 // void test() {
111 // auto d = fit::defer([]{ puts("Goodbye!"); });
112 // puts("Hello...");
113 // }
114 //
115 // // This example prints nothing because the deferred action is canceled.
116 // void do_nothing() {
117 // auto d = fit::defer([]{ puts("I'm not here."); });
118 // d.cancel();
119 // }
120 //
121 // // This example shows how the deferred action can be reassigned assuming
122 // // the new target has the same type and the old one, in this case by
123 // // representing the target as a |fit::closure|.
124 // void reassign() {
125 // auto d = fit::defer<fit::closure>([] { puts("This runs first."); });
126 // d = fit::defer<fit::closure>([] { puts("This runs afterwards."); });
127 // }
128 template <typename T>
defer(T target)129 __attribute__((__warn_unused_result__)) inline deferred_action<T> defer(T target) {
130 return deferred_action<T>(std::move(target));
131 }
132
133 // Alias for a deferred_action using a fit::callback.
134 using deferred_callback = deferred_action<fit::callback<void()>>;
135
136 // Defers execution of a fit::callback with no arguments. See |fit::defer| for
137 // details.
defer_callback(fit::callback<void ()> target)138 __attribute__((__warn_unused_result__)) inline deferred_callback defer_callback(
139 fit::callback<void()> target) {
140 return deferred_callback(std::move(target));
141 }
142
143 } // namespace fit
144
145 #endif // LIB_FIT_DEFER_H_
146