xref: /aosp_15_r20/external/pigweed/pw_toolchain/public/pw_toolchain/no_destructor.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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