1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Copyright 2018 The Fuchsia Authors. All rights reserved.
16 // Use of this source code is governed by a BSD-style license that can be
17 // found in the LICENSE file.
18
19 #pragma once
20
21 #include <utility>
22
23 #include "Function.h"
24 #include "Nullable.h"
25
26 namespace gfxstream::guest {
27 namespace fit {
28
29 // A move-only deferred action wrapper with RAII semantics.
30 // This class is not thread safe.
31 //
32 // The wrapper holds a function-like callable target with no arguments
33 // which it invokes when it goes out of scope unless canceled, called, or
34 // moved to a wrapper in a different scope.
35 //
36 // See |fit::defer()| for idiomatic usage.
37 template <typename T>
38 class DeferredAction final {
39 public:
40 // Creates a deferred action without a pending target.
41 DeferredAction() = default;
DeferredAction(decltype (nullptr))42 explicit DeferredAction(decltype(nullptr)) {}
43
44 // Creates a deferred action with a pending target.
DeferredAction(T target)45 explicit DeferredAction(T target) : mTarget(std::move(target)) {}
46
47 // Creates a deferred action with a pending target moved from another
48 // deferred action, leaving the other one without a pending target.
DeferredAction(DeferredAction && other)49 DeferredAction(DeferredAction&& other) : mTarget(std::move(other.mTarget)) {
50 other.mTarget.reset();
51 }
52
53 // Invokes and releases the deferred action's pending target (if any).
~DeferredAction()54 ~DeferredAction() { call(); }
55
56 // Returns true if the deferred action has a pending target.
57 explicit operator bool() const { return !!mTarget; }
58
59 // Invokes and releases the deferred action's pending target (if any),
60 // then move-assigns it from another deferred action, leaving the latter
61 // one without a pending target.
62 DeferredAction& operator=(DeferredAction&& other) {
63 if (&other == this)
64 return *this;
65 call();
66 mTarget = std::move(other.mTarget);
67 other.mTarget.reset();
68 return *this;
69 }
70
71 // Invokes and releases the deferred action's pending target (if any).
call()72 void call() {
73 if (mTarget) {
74 // Move to a local to guard against re-entrance.
75 T local_target = std::move(*mTarget);
76 mTarget.reset();
77 local_target();
78 }
79 }
80
81 // Releases the deferred action's pending target (if any) without
82 // invoking it.
cancel()83 void cancel() { mTarget.reset(); }
decltype(nullptr)84 DeferredAction& operator=(decltype(nullptr)) {
85 cancel();
86 return *this;
87 }
88
89 // Assigns a new target to the deferred action.
90 DeferredAction& operator=(T target) {
91 mTarget = std::move(target);
92 return *this;
93 }
94
95 DeferredAction(const DeferredAction& other) = delete;
96 DeferredAction& operator=(const DeferredAction& other) = delete;
97
98 private:
99 Nullable<T> mTarget;
100 };
101
102 template <typename T>
103 bool operator==(const DeferredAction<T>& action, decltype(nullptr)) {
104 return !action;
105 }
106 template <typename T>
107 bool operator==(decltype(nullptr), const DeferredAction<T>& action) {
108 return !action;
109 }
110 template <typename T>
111 bool operator!=(const DeferredAction<T>& action, decltype(nullptr)) {
112 return !!action;
113 }
114 template <typename T>
115 bool operator!=(decltype(nullptr), const DeferredAction<T>& action) {
116 return !!action;
117 }
118
119 // Defers execution of a function-like callable target with no arguments
120 // until the value returned by this function goes out of scope unless canceled,
121 // called, or moved to a wrapper in a different scope.
122 //
123 // // This example prints "Hello..." then "Goodbye!".
124 // void test() {
125 // auto d = fit::defer([]{ puts("Goodbye!"); });
126 // puts("Hello...");
127 // }
128 //
129 // // This example prints nothing because the deferred action is canceled.
130 // void do_nothing() {
131 // auto d = fit::defer([]{ puts("I'm not here."); });
132 // d.cancel();
133 // }
134 //
135 // // This example shows how the deferred action can be reassigned assuming
136 // // the new target has the same type and the old one, in this case by
137 // // representing the target as a |fit::Closure|.
138 // void reassign() {
139 // auto d = fit::defer<fit::Closure>([] { puts("This runs first."); });
140 // d = fit::defer<fit::Closure>([] { puts("This runs afterwards."); });
141 // }
142 template <typename T>
defer(T target)143 inline DeferredAction<T> defer(T target) {
144 return DeferredAction<T>(std::move(target));
145 }
146
147 // Alias for a deferred_action using a fit::Callback.
148 using DeferredCallback = DeferredAction<fit::Callback<void()>>;
149
150 // Defers execution of a fit::Callback with no arguments. See |fit::defer| for
151 // details.
deferCallback(fit::Callback<void ()> target)152 inline DeferredCallback deferCallback(fit::Callback<void()> target) {
153 return DeferredCallback(std::move(target));
154 }
155
156 } // namespace fit
157 } // namespace gfxstream::guest
158