1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <cstddef> 20 21 #include <functional> 22 #include <type_traits> 23 #include <utility> 24 25 #include <ftl/function.h> 26 27 namespace android::ftl { 28 29 // An RAII wrapper that invokes a function object as a finalizer when destroyed. 30 // 31 // The function object must take no arguments, and must return void. If the function object needs 32 // any context for the call, it must store it itself, for example with a lambda capture. 33 // 34 // The stored function object will be called once (unless canceled via the `cancel()` member 35 // function) at the first of: 36 // 37 // - The Finalizer instance is destroyed. 38 // - `operator()` is used to invoke the contained function. 39 // - The Finalizer instance is move-assigned a new value. The function being replaced will be 40 // invoked, and the replacement will be stored to be called later. 41 // 42 // The intent with this class is to keep cleanup code next to the code that requires that 43 // cleanup be performed. 44 // 45 // bool read_file(std::string filename) { 46 // FILE* f = fopen(filename.c_str(), "rb"); 47 // if (f == nullptr) return false; 48 // const auto cleanup = ftl::Finalizer([f]() { fclose(f); }); 49 // // fread(...), etc 50 // return true; 51 // } 52 // 53 // The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function 54 // type for storing the finalization function, such as `std::function` or `ftl::Function`. 55 // 56 // For convenience, this header defines a few useful aliases for using those types. 57 // 58 // - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>` 59 // - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>` 60 // - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>` 61 // - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>` 62 // - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>` 63 // 64 // Clients of this header are free to define other aliases they need. 65 // 66 // A Finalizer that uses a polymorphic function type can be returned from a function call and/or 67 // stored as member data (to be destroyed along with the containing class). 68 // 69 // auto register(Observer* observer) -> ftl::FinalizerStd<void()> { 70 // const auto id = observers.add(observer); 71 // return ftl::Finalizer([id]() { observers.remove(id); }); 72 // } 73 // 74 // { 75 // const auto _ = register(observer); 76 // // do the things that required the registered observer. 77 // } 78 // // the observer is removed. 79 // 80 // Cautions: 81 // 82 // 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to 83 // happen first, before the rest of the other member data is destroyed. For safety you should 84 // assume that the finalization function will access that data directly or indirectly. 85 // 86 // This means that Finalizers should be defined last, after all other normal member data in a 87 // class. 88 // 89 // class MyClass { 90 // public: 91 // bool initialize() { 92 // ready_ = true; 93 // cleanup_ = ftl::Finalizer([this]() { ready_ = false; }); 94 // return true; 95 // } 96 // 97 // bool ready_ = false; 98 // 99 // // Finalizers should be last so other class members can be accessed before being 100 // // destroyed. 101 // ftl::FinalizerStd<void()> cleanup_; 102 // }; 103 // 104 // 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you 105 // forget to do so, you are just creating a lambda that won't be automatically invoked! 106 // 107 // const auto bad = [&counter](){ ++counter; }; // Just a lambda instance 108 // const auto good = ftl::Finalizer([&counter](){ ++counter; }); 109 // 110 template <typename FinalFunction> 111 class Finalizer final { 112 // requires(std::is_invocable_r_v<void, FinalFunction>) 113 static_assert(std::is_invocable_r_v<void, FinalFunction>); 114 115 public: 116 // A default constructed Finalizer does nothing when destroyed. 117 // requires(std::is_default_constructible_v<FinalFunction>) 118 constexpr Finalizer() = default; 119 120 // Constructs a Finalizer from a function object. 121 // requires(std::is_invocable_v<F>) 122 template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>> Finalizer(F && function)123 [[nodiscard]] explicit constexpr Finalizer(F&& function) 124 : Finalizer(std::forward<F>(function), false) {} 125 ~Finalizer()126 constexpr ~Finalizer() { maybe_invoke(); } 127 128 // Disallow copying. 129 Finalizer(const Finalizer& that) = delete; 130 auto operator=(const Finalizer& that) = delete; 131 132 // Move construction 133 // requires(std::is_move_constructible_v<FinalFunction>) Finalizer(Finalizer && that)134 [[nodiscard]] constexpr Finalizer(Finalizer&& that) 135 : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} 136 137 // Implicit conversion move construction 138 // requires(!std::is_same_v<Finalizer, Finalizer<F>>) 139 template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>> 140 // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved) Finalizer(Finalizer<F> && that)141 [[nodiscard]] constexpr Finalizer(Finalizer<F>&& that) 142 : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} 143 144 // Move assignment 145 // requires(std::is_move_assignable_v<FinalFunction>) 146 constexpr auto operator=(Finalizer&& that) -> Finalizer& { 147 maybe_invoke(); 148 149 function_ = std::move(that.function_); 150 canceled_ = std::exchange(that.canceled_, true); 151 152 return *this; 153 } 154 155 // Implicit conversion move assignment 156 // requires(!std::is_same_v<Finalizer, Finalizer<F>>) 157 template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>> 158 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) 159 constexpr auto operator=(Finalizer<F>&& that) -> Finalizer& { 160 *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)); 161 return *this; 162 } 163 164 // Cancels the final function, preventing it from being invoked. cancel()165 constexpr void cancel() { 166 canceled_ = true; 167 maybe_nullify_function(); 168 } 169 170 // Invokes the final function now, if not already invoked. operator()171 constexpr void operator()() { maybe_invoke(); } 172 173 private: 174 template <typename> 175 friend class Finalizer; 176 177 template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>> Finalizer(F && function,bool canceled)178 [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled) 179 : function_(std::forward<F>(function)), canceled_(canceled) {} 180 maybe_invoke()181 constexpr void maybe_invoke() { 182 if (!std::exchange(canceled_, true)) { 183 std::invoke(function_); 184 maybe_nullify_function(); 185 } 186 } 187 maybe_nullify_function()188 constexpr void maybe_nullify_function() { 189 // Sets function_ to nullptr if that is supported for the backing type. 190 if constexpr (std::is_assignable_v<FinalFunction, nullptr_t>) { 191 function_ = nullptr; 192 } 193 } 194 195 FinalFunction function_; 196 bool canceled_ = true; 197 }; 198 199 template <typename F> 200 Finalizer(F&&) -> Finalizer<std::decay_t<F>>; 201 202 // A standard alias for using `std::function` as the polymorphic function type. 203 using FinalizerStd = Finalizer<std::function<void()>>; 204 205 // Helpful aliases for using `ftl::Function` as the polymorphic function type. 206 using FinalizerFtl = Finalizer<Function<void()>>; 207 using FinalizerFtl1 = Finalizer<Function<void(), 1>>; 208 using FinalizerFtl2 = Finalizer<Function<void(), 2>>; 209 using FinalizerFtl3 = Finalizer<Function<void(), 3>>; 210 211 } // namespace android::ftl