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