1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_NO_DESTRUCTOR_H_
6 #define PARTITION_ALLOC_PARTITION_ALLOC_BASE_NO_DESTRUCTOR_H_
7 
8 #include <new>
9 #include <type_traits>
10 #include <utility>
11 
12 namespace partition_alloc::internal::base {
13 
14 // Helper type to create a function-local static variable of type `T` when `T`
15 // has a non-trivial destructor. Storing a `T` in a `base::NoDestructor<T>` will
16 // prevent `~T()` from running, even when the variable goes out of scope.
17 //
18 // Useful when a variable has static storage duration but its type has a
19 // non-trivial destructor. Chromium bans global constructors and destructors:
20 // using a function-local static variable prevents the former, while using
21 // `base::NoDestructor<T>` prevents the latter.
22 //
23 // ## Caveats
24 //
25 // - Must only be used as a function-local static variable. Declaring a global
26 //   variable of type `base::NoDestructor<T>` will still generate a global
27 //   constructor; declaring a local or member variable will lead to memory leaks
28 //   or other surprising and undesirable behaviour.
29 //
30 // - If the data is rarely used, consider creating it on demand rather than
31 //   caching it for the lifetime of the program. Though `base::NoDestructor<T>`
32 //   does not heap allocate, the compiler still reserves space in bss for
33 //   storing `T`, which costs memory at runtime.
34 //
35 // - If `T` is trivially destructible, do not use `base::NoDestructor<T>`:
36 //
37 //     const uint64_t GetUnstableSessionSeed() {
38 //       // No need to use `base::NoDestructor<T>` as `uint64_t` is trivially
39 //       // destructible and does not require a global destructor.
40 //       static const uint64_t kSessionSeed = base::RandUint64();
41 //       return kSessionSeed;
42 //     }
43 //
44 // ## Example Usage
45 //
46 // const std::string& GetDefaultText() {
47 //   // Required since `static const std::string` requires a global destructor.
48 //   static const base::NoDestructor<std::string> s("Hello world!");
49 //   return *s;
50 // }
51 //
52 // More complex initialization using a lambda:
53 //
54 // const std::string& GetRandomNonce() {
55 //   // `nonce` is initialized with random data the first time this function is
56 //   // called, but its value is fixed thereafter.
57 //   static const base::NoDestructor<std::string> nonce([] {
58 //     std::string s(16);
59 //     crypto::RandString(s.data(), s.size());
60 //     return s;
61 //   }());
62 //   return *nonce;
63 // }
64 //
65 // ## Thread safety
66 //
67 // Initialisation of function-local static variables is thread-safe since C++11.
68 // The standard guarantees that:
69 //
70 // - function-local static variables will be initialised the first time
71 //   execution passes through the declaration.
72 //
73 // - if another thread's execution concurrently passes through the declaration
74 //   in the middle of initialisation, that thread will wait for the in-progress
75 //   initialisation to complete.
76 template <typename T>
77 class NoDestructor {
78  public:
79   template <typename... Args>
NoDestructor(Args &&...args)80   constexpr explicit NoDestructor(Args&&... args)
81       : storage_(std::forward<Args>(args)...) {}
82 
83   // Allows copy and move construction of the contained type, to allow
84   // construction from an initializer list, e.g. for std::vector.
NoDestructor(const T & x)85   explicit NoDestructor(const T& x) : storage_(x) {}
NoDestructor(T && x)86   explicit NoDestructor(T&& x) : storage_(std::move(x)) {}
87 
88   NoDestructor(const NoDestructor&) = delete;
89   NoDestructor& operator=(const NoDestructor&) = delete;
90 
91   ~NoDestructor() = default;
92 
93   const T& operator*() const { return *storage_.get()(); }
94   T& operator*() { return *storage_.get(); }
95 
96   const T* operator->() const { return storage_.get()(); }
97   T* operator->() { return storage_.get(); }
98 
get()99   const T* get() const { return storage_.get(); }
get()100   T* get() { return storage_.get(); }
101 
102  private:
103   // Do not friend this. This is an implementation detail.
104   class DirectStorage {
105    public:
106     template <typename... Args>
DirectStorage(Args &&...args)107     constexpr explicit DirectStorage(Args&&... args)
108         : storage_(std::forward<Args>(args)...) {}
109 
get()110     const T* get() const { return &storage_; }
get()111     T* get() { return &storage_; }
112 
113    private:
114     T storage_;
115   };
116 
117   // Do not friend this. This is an implementation detail.
118   class PlacementStorage {
119    public:
120     template <typename... Args>
PlacementStorage(Args &&...args)121     explicit PlacementStorage(Args&&... args) {
122       new (storage_) T(std::forward<Args>(args)...);
123     }
124 
get()125     const T* get() const { return const_cast<PlacementStorage*>(this)->get(); }
get()126     T* get() { return reinterpret_cast<T*>(storage_); }
127 
128    private:
129     alignas(T) char storage_[sizeof(T)];
130   };
131 
132   // C++20 provides a constexpr `std::construct_at`, so in theory, both branches
133   // could use PlacementStorage. There are some advantages to providing
134   // `DirectStorage` though; it can avoid the need to have runtime once-init
135   // tracking at all.
136   std::conditional_t<std::is_trivially_destructible_v<T>,
137                      DirectStorage,
138                      PlacementStorage>
139       storage_;
140 };
141 
142 }  // namespace partition_alloc::internal::base
143 
144 #endif  // PARTITION_ALLOC_PARTITION_ALLOC_BASE_NO_DESTRUCTOR_H_
145