blob: 9c035c1b651f68f7da90bcc30b1ff81b56bcfee0 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2018 The Chromium Authors
Daniel Cheng73999fe62018-01-19 00:28:152// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_NO_DESTRUCTOR_H_
6#define BASE_NO_DESTRUCTOR_H_
7
Daniel Chengbae24732018-02-14 19:42:148#include <new>
Avi Drissmanded77172021-07-02 18:23:009#include <type_traits>
Daniel Cheng73999fe62018-01-19 00:28:1510#include <utility>
11
12namespace base {
13
Daniel Cheng117c6a92022-10-10 23:47:2814// 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.
Daniel Cheng73999fe62018-01-19 00:28:1517//
Daniel Cheng117c6a92022-10-10 23:47:2818// 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//
John Stiles7b795642023-12-07 21:55:1125// - Must not be used for locals or fields; by definition, this does not run
26// destructors, and this will likely lead to memory leaks and other
27// surprising and undesirable behaviour.
28//
29// - If `T` is not constexpr constructible, must be a function-local static
30// variable, since a global `NoDestructor<T>` will still generate a static
31// initializer.
32//
33// - If `T` is constinit constructible, may be used as a global, but mark the
34// global `constinit`.
Daniel Cheng117c6a92022-10-10 23:47:2835//
36// - If the data is rarely used, consider creating it on demand rather than
37// caching it for the lifetime of the program. Though `base::NoDestructor<T>`
38// does not heap allocate, the compiler still reserves space in bss for
39// storing `T`, which costs memory at runtime.
40//
41// - If `T` is trivially destructible, do not use `base::NoDestructor<T>`:
42//
43// const uint64_t GetUnstableSessionSeed() {
44// // No need to use `base::NoDestructor<T>` as `uint64_t` is trivially
45// // destructible and does not require a global destructor.
46// static const uint64_t kSessionSeed = base::RandUint64();
47// return kSessionSeed;
48// }
49//
50// ## Example Usage
51//
52// const std::string& GetDefaultText() {
53// // Required since `static const std::string` requires a global destructor.
54// static const base::NoDestructor<std::string> s("Hello world!");
Daniel Chengbae24732018-02-14 19:42:1455// return *s;
Daniel Cheng73999fe62018-01-19 00:28:1556// }
57//
Daniel Cheng117c6a92022-10-10 23:47:2858// More complex initialization using a lambda:
59//
60// const std::string& GetRandomNonce() {
61// // `nonce` is initialized with random data the first time this function is
62// // called, but its value is fixed thereafter.
Daniel Cheng73999fe62018-01-19 00:28:1563// static const base::NoDestructor<std::string> nonce([] {
64// std::string s(16);
65// crypto::RandString(s.data(), s.size());
66// return s;
Peter Kasting602defc2018-02-16 03:21:5467// }());
Daniel Cheng73999fe62018-01-19 00:28:1568// return *nonce;
69// }
70//
Daniel Cheng117c6a92022-10-10 23:47:2871// ## Thread safety
Daniel Cheng73999fe62018-01-19 00:28:1572//
Daniel Cheng117c6a92022-10-10 23:47:2873// Initialisation of function-local static variables is thread-safe since C++11.
74// The standard guarantees that:
75//
76// - function-local static variables will be initialised the first time
77// execution passes through the declaration.
78//
79// - if another thread's execution concurrently passes through the declaration
80// in the middle of initialisation, that thread will wait for the in-progress
81// initialisation to complete.
82template <typename T>
Daniel Cheng73999fe62018-01-19 00:28:1583class NoDestructor {
84 public:
John Stiles7b795642023-12-07 21:55:1185 static_assert(!(std::is_trivially_constructible_v<T> &&
86 std::is_trivially_destructible_v<T>),
87 "T is trivially constructible and destructible; please use a "
88 "constinit object of type T directly instead");
89
Avi Drissmanded77172021-07-02 18:23:0090 static_assert(
Daniel Cheng117c6a92022-10-10 23:47:2891 !std::is_trivially_destructible_v<T>,
92 "T is trivially destructible; please use a function-local static "
93 "of type T directly instead");
Avi Drissmanded77172021-07-02 18:23:0094
Daniel Cheng73999fe62018-01-19 00:28:1595 // Not constexpr; just write static constexpr T x = ...; if the value should
96 // be a constexpr.
97 template <typename... Args>
98 explicit NoDestructor(Args&&... args) {
Daniel Chengbae24732018-02-14 19:42:1499 new (storage_) T(std::forward<Args>(args)...);
Daniel Cheng73999fe62018-01-19 00:28:15100 }
101
Daniel Chengbae24732018-02-14 19:42:14102 // Allows copy and move construction of the contained type, to allow
103 // construction from an initializer list, e.g. for std::vector.
104 explicit NoDestructor(const T& x) { new (storage_) T(x); }
105 explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
106
107 NoDestructor(const NoDestructor&) = delete;
108 NoDestructor& operator=(const NoDestructor&) = delete;
109
Daniel Cheng73999fe62018-01-19 00:28:15110 ~NoDestructor() = default;
111
112 const T& operator*() const { return *get(); }
113 T& operator*() { return *get(); }
114
115 const T* operator->() const { return get(); }
116 T* operator->() { return get(); }
117
Daniel Chengbae24732018-02-14 19:42:14118 const T* get() const { return reinterpret_cast<const T*>(storage_); }
119 T* get() { return reinterpret_cast<T*>(storage_); }
Daniel Cheng73999fe62018-01-19 00:28:15120
121 private:
122 alignas(T) char storage_[sizeof(T)];
Daniel Chengbae24732018-02-14 19:42:14123
124#if defined(LEAK_SANITIZER)
Alison Gale53c77f62024-04-22 15:16:27125 // TODO(crbug.com/40562930): This is a hack to work around the fact
Daniel Chengbae24732018-02-14 19:42:14126 // that LSan doesn't seem to treat NoDestructor as a root for reachability
127 // analysis. This means that code like this:
128 // static base::NoDestructor<std::vector<int>> v({1, 2, 3});
129 // is considered a leak. Using the standard leak sanitizer annotations to
130 // suppress leaks doesn't work: std::vector is implicitly constructed before
131 // calling the base::NoDestructor constructor.
132 //
133 // Unfortunately, I haven't been able to demonstrate this issue in simpler
134 // reproductions: until that's resolved, hold an explicit pointer to the
135 // placement-new'd object in leak sanitizer mode to help LSan realize that
136 // objects allocated by the contained type are still reachable.
137 T* storage_ptr_ = reinterpret_cast<T*>(storage_);
138#endif // defined(LEAK_SANITIZER)
Daniel Cheng73999fe62018-01-19 00:28:15139};
140
141} // namespace base
142
143#endif // BASE_NO_DESTRUCTOR_H_