1 // Copyright 2021 The Abseil Authors. 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 // https://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 #ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_ 16 #define ABSL_CLEANUP_INTERNAL_CLEANUP_H_ 17 18 #include <new> 19 #include <type_traits> 20 #include <utility> 21 22 #include "absl/base/internal/invoke.h" 23 #include "absl/base/macros.h" 24 #include "absl/base/thread_annotations.h" 25 #include "absl/utility/utility.h" 26 27 namespace absl { 28 ABSL_NAMESPACE_BEGIN 29 30 namespace cleanup_internal { 31 32 struct Tag {}; 33 34 template <typename Arg, typename... Args> WasDeduced()35constexpr bool WasDeduced() { 36 return (std::is_same<cleanup_internal::Tag, Arg>::value) && 37 (sizeof...(Args) == 0); 38 } 39 40 template <typename Callback> ReturnsVoid()41constexpr bool ReturnsVoid() { 42 return (std::is_same<base_internal::invoke_result_t<Callback>, void>::value); 43 } 44 45 template <typename Callback> 46 class Storage { 47 public: 48 Storage() = delete; 49 Storage(Callback callback)50 explicit Storage(Callback callback) { 51 // Placement-new into a character buffer is used for eager destruction when 52 // the cleanup is invoked or cancelled. To ensure this optimizes well, the 53 // behavior is implemented locally instead of using an absl::optional. 54 ::new (GetCallbackBuffer()) Callback(std::move(callback)); 55 is_callback_engaged_ = true; 56 } 57 Storage(Storage && other)58 Storage(Storage&& other) { 59 ABSL_HARDENING_ASSERT(other.IsCallbackEngaged()); 60 61 ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback())); 62 is_callback_engaged_ = true; 63 64 other.DestroyCallback(); 65 } 66 67 Storage(const Storage& other) = delete; 68 69 Storage& operator=(Storage&& other) = delete; 70 71 Storage& operator=(const Storage& other) = delete; 72 GetCallbackBuffer()73 void* GetCallbackBuffer() { return static_cast<void*>(+callback_buffer_); } 74 GetCallback()75 Callback& GetCallback() { 76 return *reinterpret_cast<Callback*>(GetCallbackBuffer()); 77 } 78 IsCallbackEngaged()79 bool IsCallbackEngaged() const { return is_callback_engaged_; } 80 DestroyCallback()81 void DestroyCallback() { 82 is_callback_engaged_ = false; 83 GetCallback().~Callback(); 84 } 85 InvokeCallback()86 void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS { 87 std::move(GetCallback())(); 88 } 89 90 private: 91 bool is_callback_engaged_; 92 alignas(Callback) char callback_buffer_[sizeof(Callback)]; 93 }; 94 95 } // namespace cleanup_internal 96 97 ABSL_NAMESPACE_END 98 } // namespace absl 99 100 #endif // ABSL_CLEANUP_INTERNAL_CLEANUP_H_ 101