1 // Copyright 2022 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // 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, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <new> 17 #include <type_traits> 18 #include <utility> 19 20 namespace pw { 21 22 // Helper type to create a function-local static variable of type T when T has a 23 // non-trivial destructor. Storing a T in a pw::NoDestructor<T> will prevent 24 // ~T() from running, even when the variable goes out of scope. 25 // 26 // This class is useful when a variable has static storage duration but its type 27 // has a non-trivial destructor. Destructor ordering is not defined and can 28 // cause issues in multithreaded environments. Additionally, removing destructor 29 // calls can save code size. 30 // 31 // Except in generic code, do not use pw::NoDestructor<T> with trivially 32 // destructible types. Use the type directly instead. If the variable can be 33 // constexpr, make it constexpr. 34 // 35 // pw::NoDestructor<T> provides a similar API to std::optional. Use * or -> to 36 // access the wrapped type. 37 // 38 // Example usage: 39 // 40 // pw::sync::Mutex& GetMutex() { 41 // // Use NoDestructor to avoid running the mutex destructor when exit-time 42 // // destructors run. 43 // static const pw::NoDestructor<pw::sync::Mutex> global_mutex; 44 // return *global_mutex; 45 // } 46 // 47 // WARNING: Misuse of NoDestructor can cause memory leaks and other problems. 48 // Only skip destructors when you know it is safe to do so. 49 // 50 // In Clang, pw::NoDestructor can be replaced with the [[clang::no_destroy]] 51 // attribute. 52 template <typename T> 53 class NoDestructor { 54 public: 55 using value_type = T; 56 57 // Initializes a T in place. 58 // 59 // This overload is disabled when it might collide with copy/move. 60 template <typename... Args, 61 typename std::enable_if<!std::is_same<void(std::decay_t<Args>&...), 62 void(NoDestructor&)>::value, 63 int>::type = 0> NoDestructor(Args &&...args)64 explicit constexpr NoDestructor(Args&&... args) 65 : storage_(std::forward<Args>(args)...) {} 66 67 // Move or copy from the contained type. This allows for construction from an 68 // initializer list, e.g. for std::vector. NoDestructor(const T & x)69 explicit constexpr NoDestructor(const T& x) : storage_(x) {} NoDestructor(T && x)70 explicit constexpr NoDestructor(T&& x) : storage_(std::move(x)) {} 71 72 NoDestructor(const NoDestructor&) = delete; 73 NoDestructor& operator=(const NoDestructor&) = delete; 74 75 ~NoDestructor() = default; 76 77 const T& operator*() const { return *storage_.get(); } 78 T& operator*() { return *storage_.get(); } 79 80 const T* operator->() const { return storage_.get(); } 81 T* operator->() { return storage_.get(); } 82 83 private: 84 class DirectStorage { 85 public: 86 template <typename... Args> DirectStorage(Args &&...args)87 explicit constexpr DirectStorage(Args&&... args) 88 : value_(std::forward<Args>(args)...) {} 89 get()90 const T* get() const { return &value_; } get()91 T* get() { return &value_; } 92 93 private: 94 T value_; 95 }; 96 97 class PlacementStorage { 98 public: 99 template <typename... Args> PlacementStorage(Args &&...args)100 explicit PlacementStorage(Args&&... args) { 101 new (&memory_) T(std::forward<Args>(args)...); 102 } 103 get()104 const T* get() const { 105 return std::launder(reinterpret_cast<const T*>(&memory_)); 106 } get()107 T* get() { return std::launder(reinterpret_cast<T*>(&memory_)); } 108 109 private: 110 alignas(T) char memory_[sizeof(T)]; 111 }; 112 113 // If the type is already trivially destructible, use it directly. Trivially 114 // destructible types do not need NoDestructor, but NoDestructor supports them 115 // to work better with generic code. 116 std::conditional_t<std::is_trivially_destructible<T>::value, 117 DirectStorage, 118 PlacementStorage> 119 storage_; 120 }; 121 122 } // namespace pw 123