• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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