xref: /aosp_15_r20/external/pigweed/third_party/fuchsia/repo/sdk/lib/fit/include/lib/fit/defer.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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