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` 23 /// has a non-trivial destructor. Storing a `T` in a `pw::NoDestructor<T>` will 24 /// prevent `~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 27 /// type has a non-trivial destructor. Destructor ordering is not defined and 28 /// can cause issues in multithreaded environments. Additionally, removing 29 /// destructor 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 36 /// `->` to access the wrapped type. 37 /// 38 /// Example usage: 39 /// @code{.cpp} 40 /// 41 /// pw::sync::Mutex& GetMutex() { 42 /// // Use NoDestructor to avoid running the mutex destructor when exit-time 43 /// // destructors run. 44 /// static const pw::NoDestructor<pw::sync::Mutex> global_mutex; 45 /// return *global_mutex; 46 /// } 47 /// 48 /// @endcode 49 /// 50 /// In Clang, `pw::NoDestructor` can be replaced with the 51 /// <a href="https://clang.llvm.org/docs/AttributeReference.html#no-destroy"> 52 /// [[clang::no_destroy]]</a> attribute. `pw::NoDestructor<T>` is similar to 53 /// Chromium’s `base::NoDestructor<T>` in <a 54 /// href="https://chromium.googlesource.com/chromium/src/base/+/5ea6e31f927aa335bfceb799a2007c7f9007e680/no_destructor.h"> 55 /// src/base/no_destructor.h</a>. 56 /// 57 /// @warning Misuse of NoDestructor can cause memory leaks and other problems. 58 /// Only skip destructors when you know it is safe to do so. 59 template <typename T> 60 class NoDestructor { 61 public: 62 using value_type = T; 63 64 // Initializes a T in place. 65 // 66 // This overload is disabled when it might collide with copy/move. 67 template <typename... Args, 68 typename std::enable_if<!std::is_same<void(std::decay_t<Args>&...), 69 void(NoDestructor&)>::value, 70 int>::type = 0> NoDestructor(Args &&...args)71 explicit constexpr NoDestructor(Args&&... args) 72 : storage_(std::forward<Args>(args)...) {} 73 74 // Move or copy from the contained type. This allows for construction from an 75 // initializer list, e.g. for std::vector. NoDestructor(const T & x)76 explicit constexpr NoDestructor(const T& x) : storage_(x) {} NoDestructor(T && x)77 explicit constexpr NoDestructor(T&& x) : storage_(std::move(x)) {} 78 79 NoDestructor(const NoDestructor&) = delete; 80 NoDestructor& operator=(const NoDestructor&) = delete; 81 82 ~NoDestructor() = default; 83 84 const T& operator*() const { return *storage_.get(); } 85 T& operator*() { return *storage_.get(); } 86 87 const T* operator->() const { return storage_.get(); } 88 T* operator->() { return storage_.get(); } 89 90 private: 91 class DirectStorage { 92 public: 93 template <typename... Args> DirectStorage(Args &&...args)94 explicit constexpr DirectStorage(Args&&... args) 95 : value_(std::forward<Args>(args)...) {} 96 get()97 const T* get() const { return &value_; } get()98 T* get() { return &value_; } 99 100 private: 101 T value_; 102 }; 103 104 class PlacementStorage { 105 public: 106 template <typename... Args> PlacementStorage(Args &&...args)107 explicit PlacementStorage(Args&&... args) { 108 new (&memory_) T(std::forward<Args>(args)...); 109 } 110 get()111 const T* get() const { 112 return std::launder(reinterpret_cast<const T*>(&memory_)); 113 } get()114 T* get() { return std::launder(reinterpret_cast<T*>(&memory_)); } 115 116 private: 117 alignas(T) char memory_[sizeof(T)]; 118 }; 119 120 // If the type is already trivially destructible, use it directly. Trivially 121 // destructible types do not need NoDestructor, but NoDestructor supports them 122 // to work better with generic code. 123 std::conditional_t<std::is_trivially_destructible<T>::value, 124 DirectStorage, 125 PlacementStorage> 126 storage_; 127 }; 128 129 } // namespace pw 130