/src/abseil-cpp/absl/base/no_destructor.h
Line | Count | Source |
1 | | // Copyright 2023 The Abseil Authors. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of 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, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | // ----------------------------------------------------------------------------- |
16 | | // File: no_destructor.h |
17 | | // ----------------------------------------------------------------------------- |
18 | | // |
19 | | // This header file defines the absl::NoDestructor<T> wrapper for defining a |
20 | | // static type that does not need to be destructed upon program exit. Instead, |
21 | | // such an object survives during program exit (and can be safely accessed at |
22 | | // any time). |
23 | | // |
24 | | // absl::NoDestructor<T> is useful when when a variable has static storage |
25 | | // duration but its type has a non-trivial destructor. Global constructors are |
26 | | // not recommended because of the C++'s static initialization order fiasco (See |
27 | | // https://en.cppreference.com/w/cpp/language/siof). Global destructors are not |
28 | | // allowed due to similar concerns about destruction ordering. Using |
29 | | // absl::NoDestructor<T> as a function-local static prevents both of these |
30 | | // issues. |
31 | | // |
32 | | // See below for complete details. |
33 | | |
34 | | |
35 | | #ifndef ABSL_BASE_NO_DESTRUCTOR_H_ |
36 | | #define ABSL_BASE_NO_DESTRUCTOR_H_ |
37 | | |
38 | | #include <new> |
39 | | #include <type_traits> |
40 | | #include <utility> |
41 | | |
42 | | #include "absl/base/config.h" |
43 | | #include "absl/base/nullability.h" |
44 | | |
45 | | namespace absl { |
46 | | ABSL_NAMESPACE_BEGIN |
47 | | |
48 | | // absl::NoDestructor<T> |
49 | | // |
50 | | // NoDestructor<T> is a wrapper around an object of type T that behaves as an |
51 | | // object of type T but never calls T's destructor. NoDestructor<T> makes it |
52 | | // safer and/or more efficient to use such objects in static storage contexts, |
53 | | // ideally as function scope static variables. |
54 | | // |
55 | | // An instance of absl::NoDestructor<T> has similar type semantics to an |
56 | | // instance of T: |
57 | | // |
58 | | // * Constructs in the same manner as an object of type T through perfect |
59 | | // forwarding. |
60 | | // * Provides pointer/reference semantic access to the object of type T via |
61 | | // `->`, `*`, and `get()`. |
62 | | // (Note that `const NoDestructor<T>` works like a pointer to const `T`.) |
63 | | // |
64 | | // Additionally, NoDestructor<T> provides the following benefits: |
65 | | // |
66 | | // * Never calls T's destructor for the object |
67 | | // * If the object is a function-local static variable, the type can be |
68 | | // lazily constructed. |
69 | | // |
70 | | // An object of type NoDestructor<T> is "trivially destructible" in the notion |
71 | | // that its destructor is never run. |
72 | | // |
73 | | // Usage as Function Scope Static Variables |
74 | | // |
75 | | // Function static objects will be lazily initialized within static storage: |
76 | | // |
77 | | // // Function scope. |
78 | | // const std::string& MyString() { |
79 | | // static const absl::NoDestructor<std::string> x("foo"); |
80 | | // return *x; |
81 | | // } |
82 | | // |
83 | | // For function static variables, NoDestructor avoids heap allocation and can be |
84 | | // inlined in static storage, resulting in exactly-once, thread-safe |
85 | | // construction of an object, and very fast access thereafter (the cost is a few |
86 | | // extra cycles). |
87 | | // |
88 | | // Using NoDestructor<T> in this manner is generally better than other patterns |
89 | | // which require pointer chasing: |
90 | | // |
91 | | // // Prefer using absl::NoDestructor<T> instead for the static variable. |
92 | | // const std::string& MyString() { |
93 | | // static const std::string* x = new std::string("foo"); |
94 | | // return *x; |
95 | | // } |
96 | | // |
97 | | // Usage as Global Static Variables |
98 | | // |
99 | | // NoDestructor<T> allows declaration of a global object of type T that has a |
100 | | // non-trivial destructor since its destructor is never run. However, such |
101 | | // objects still need to worry about initialization order, so such use is not |
102 | | // recommended, strongly discouraged by the Google C++ Style Guide, and outright |
103 | | // banned in Chromium. |
104 | | // See https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables |
105 | | // |
106 | | // // Global or namespace scope. |
107 | | // absl::NoDestructor<MyRegistry> reg{"foo", "bar", 8008}; |
108 | | // |
109 | | // Note that if your object already has a trivial destructor, you don't need to |
110 | | // use NoDestructor<T>. |
111 | | // |
112 | | template <typename T> |
113 | | class NoDestructor { |
114 | | public: |
115 | | // Forwards arguments to the T's constructor: calls T(args...). |
116 | | template <typename... Ts, |
117 | | // Disable this overload when it might collide with copy/move. |
118 | | typename std::enable_if<!std::is_same<void(std::decay_t<Ts>&...), |
119 | | void(NoDestructor&)>::value, |
120 | | int>::type = 0> |
121 | | explicit constexpr NoDestructor(Ts&&... args) |
122 | 4 | : impl_(std::forward<Ts>(args)...) {} absl::NoDestructor<absl::flags_internal::FlagRegistry>::NoDestructor<, 0>() Line | Count | Source | 122 | 2 | : impl_(std::forward<Ts>(args)...) {} |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::NoDestructor<, 0>() Line | Count | Source | 122 | 1 | : impl_(std::forward<Ts>(args)...) {} |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::NoDestructor<, 0>() Line | Count | Source | 122 | 1 | : impl_(std::forward<Ts>(args)...) {} |
Unexecuted instantiation: absl::NoDestructor<absl::Mutex>::NoDestructor<, 0>() |
123 | | |
124 | | // Forwards copy and move construction for T. Enables usage like this: |
125 | | // static NoDestructor<std::array<string, 3>> x{{{"1", "2", "3"}}}; |
126 | | // static NoDestructor<std::vector<int>> x{{1, 2, 3}}; |
127 | | explicit constexpr NoDestructor(const T& x) : impl_(x) {} |
128 | | explicit constexpr NoDestructor(T&& x) |
129 | | : impl_(std::move(x)) {} |
130 | | |
131 | | // No copying. |
132 | | NoDestructor(const NoDestructor&) = delete; |
133 | | NoDestructor& operator=(const NoDestructor&) = delete; |
134 | | |
135 | | // Pretend to be a smart pointer to T with deep constness. |
136 | | // Never returns a null pointer. |
137 | 3.79M | T& operator*() { return *get(); } absl::NoDestructor<absl::flags_internal::FlagRegistry>::operator*() Line | Count | Source | 137 | 16 | T& operator*() { return *get(); } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::operator*() Line | Count | Source | 137 | 3.79M | T& operator*() { return *get(); } |
|
138 | | absl::Nonnull<T*> operator->() { return get(); } |
139 | 3.79M | absl::Nonnull<T*> get() { return impl_.get(); } absl::NoDestructor<absl::flags_internal::FlagRegistry>::get() Line | Count | Source | 139 | 16 | absl::Nonnull<T*> get() { return impl_.get(); } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::get() Line | Count | Source | 139 | 1 | absl::Nonnull<T*> get() { return impl_.get(); } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::get() Line | Count | Source | 139 | 3.79M | absl::Nonnull<T*> get() { return impl_.get(); } |
Unexecuted instantiation: absl::NoDestructor<absl::Mutex>::get() |
140 | | const T& operator*() const { return *get(); } |
141 | | absl::Nonnull<const T*> operator->() const { return get(); } |
142 | | absl::Nonnull<const T*> get() const { return impl_.get(); } |
143 | | |
144 | | private: |
145 | | class DirectImpl { |
146 | | public: |
147 | | template <typename... Args> |
148 | | explicit constexpr DirectImpl(Args&&... args) |
149 | | : value_(std::forward<Args>(args)...) {} |
150 | | absl::Nonnull<const T*> get() const { return &value_; } |
151 | | absl::Nonnull<T*> get() { return &value_; } |
152 | | |
153 | | private: |
154 | | T value_; |
155 | | }; |
156 | | |
157 | | class PlacementImpl { |
158 | | public: |
159 | | template <typename... Args> |
160 | 4 | explicit PlacementImpl(Args&&... args) { |
161 | 4 | new (&space_) T(std::forward<Args>(args)...); |
162 | 4 | } absl::NoDestructor<absl::flags_internal::FlagRegistry>::PlacementImpl::PlacementImpl<>() Line | Count | Source | 160 | 2 | explicit PlacementImpl(Args&&... args) { | 161 | 2 | new (&space_) T(std::forward<Args>(args)...); | 162 | 2 | } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::PlacementImpl::PlacementImpl<>() Line | Count | Source | 160 | 1 | explicit PlacementImpl(Args&&... args) { | 161 | 1 | new (&space_) T(std::forward<Args>(args)...); | 162 | 1 | } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::PlacementImpl::PlacementImpl<>() Line | Count | Source | 160 | 1 | explicit PlacementImpl(Args&&... args) { | 161 | 1 | new (&space_) T(std::forward<Args>(args)...); | 162 | 1 | } |
Unexecuted instantiation: absl::NoDestructor<absl::Mutex>::PlacementImpl::PlacementImpl<>() |
163 | | absl::Nonnull<const T*> get() const { |
164 | | return Launder(reinterpret_cast<const T*>(&space_)); |
165 | | } |
166 | 3.79M | absl::Nonnull<T*> get() { return Launder(reinterpret_cast<T*>(&space_)); } absl::NoDestructor<absl::flags_internal::FlagRegistry>::PlacementImpl::get() Line | Count | Source | 166 | 16 | absl::Nonnull<T*> get() { return Launder(reinterpret_cast<T*>(&space_)); } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::PlacementImpl::get() Line | Count | Source | 166 | 1 | absl::Nonnull<T*> get() { return Launder(reinterpret_cast<T*>(&space_)); } |
log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::PlacementImpl::get() Line | Count | Source | 166 | 3.79M | absl::Nonnull<T*> get() { return Launder(reinterpret_cast<T*>(&space_)); } |
Unexecuted instantiation: absl::NoDestructor<absl::Mutex>::PlacementImpl::get() |
167 | | |
168 | | private: |
169 | | template <typename P> |
170 | 3.79M | static absl::Nonnull<P*> Launder(absl::Nonnull<P*> p) { |
171 | 3.79M | #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L |
172 | 3.79M | return std::launder(p); |
173 | | #elif ABSL_HAVE_BUILTIN(__builtin_launder) |
174 | | return __builtin_launder(p); |
175 | | #else |
176 | | // When `std::launder` or equivalent are not available, we rely on |
177 | | // undefined behavior, which works as intended on Abseil's officially |
178 | | // supported platforms as of Q3 2023. |
179 | | #if defined(__GNUC__) && !defined(__clang__) |
180 | | #pragma GCC diagnostic push |
181 | | #pragma GCC diagnostic ignored "-Wstrict-aliasing" |
182 | | #endif |
183 | | return p; |
184 | | #if defined(__GNUC__) && !defined(__clang__) |
185 | | #pragma GCC diagnostic pop |
186 | | #endif |
187 | | #endif |
188 | 3.79M | } absl::flags_internal::FlagRegistry* absl::NoDestructor<absl::flags_internal::FlagRegistry>::PlacementImpl::Launder<absl::flags_internal::FlagRegistry>(absl::flags_internal::FlagRegistry*) Line | Count | Source | 170 | 16 | static absl::Nonnull<P*> Launder(absl::Nonnull<P*> p) { | 171 | 16 | #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L | 172 | 16 | return std::launder(p); | 173 | | #elif ABSL_HAVE_BUILTIN(__builtin_launder) | 174 | | return __builtin_launder(p); | 175 | | #else | 176 | | // When `std::launder` or equivalent are not available, we rely on | 177 | | // undefined behavior, which works as intended on Abseil's officially | 178 | | // supported platforms as of Q3 2023. | 179 | | #if defined(__GNUC__) && !defined(__clang__) | 180 | | #pragma GCC diagnostic push | 181 | | #pragma GCC diagnostic ignored "-Wstrict-aliasing" | 182 | | #endif | 183 | | return p; | 184 | | #if defined(__GNUC__) && !defined(__clang__) | 185 | | #pragma GCC diagnostic pop | 186 | | #endif | 187 | | #endif | 188 | 16 | } |
log_sink_set.cc:absl::log_internal::(anonymous namespace)::StderrLogSink* absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::PlacementImpl::Launder<absl::log_internal::(anonymous namespace)::StderrLogSink>(absl::log_internal::(anonymous namespace)::StderrLogSink*) Line | Count | Source | 170 | 1 | static absl::Nonnull<P*> Launder(absl::Nonnull<P*> p) { | 171 | 1 | #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L | 172 | 1 | return std::launder(p); | 173 | | #elif ABSL_HAVE_BUILTIN(__builtin_launder) | 174 | | return __builtin_launder(p); | 175 | | #else | 176 | | // When `std::launder` or equivalent are not available, we rely on | 177 | | // undefined behavior, which works as intended on Abseil's officially | 178 | | // supported platforms as of Q3 2023. | 179 | | #if defined(__GNUC__) && !defined(__clang__) | 180 | | #pragma GCC diagnostic push | 181 | | #pragma GCC diagnostic ignored "-Wstrict-aliasing" | 182 | | #endif | 183 | | return p; | 184 | | #if defined(__GNUC__) && !defined(__clang__) | 185 | | #pragma GCC diagnostic pop | 186 | | #endif | 187 | | #endif | 188 | 1 | } |
log_sink_set.cc:absl::log_internal::(anonymous namespace)::GlobalLogSinkSet* absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::PlacementImpl::Launder<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>(absl::log_internal::(anonymous namespace)::GlobalLogSinkSet*) Line | Count | Source | 170 | 3.79M | static absl::Nonnull<P*> Launder(absl::Nonnull<P*> p) { | 171 | 3.79M | #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L | 172 | 3.79M | return std::launder(p); | 173 | | #elif ABSL_HAVE_BUILTIN(__builtin_launder) | 174 | | return __builtin_launder(p); | 175 | | #else | 176 | | // When `std::launder` or equivalent are not available, we rely on | 177 | | // undefined behavior, which works as intended on Abseil's officially | 178 | | // supported platforms as of Q3 2023. | 179 | | #if defined(__GNUC__) && !defined(__clang__) | 180 | | #pragma GCC diagnostic push | 181 | | #pragma GCC diagnostic ignored "-Wstrict-aliasing" | 182 | | #endif | 183 | | return p; | 184 | | #if defined(__GNUC__) && !defined(__clang__) | 185 | | #pragma GCC diagnostic pop | 186 | | #endif | 187 | | #endif | 188 | 3.79M | } |
Unexecuted instantiation: absl::Mutex* absl::NoDestructor<absl::Mutex>::PlacementImpl::Launder<absl::Mutex>(absl::Mutex*) |
189 | | |
190 | | alignas(T) unsigned char space_[sizeof(T)]; |
191 | | }; |
192 | | |
193 | | // If the object is trivially destructible we use a member directly to avoid |
194 | | // potential once-init runtime initialization. It somewhat defeats the |
195 | | // purpose of NoDestructor in this case, but this makes the class more |
196 | | // friendly to generic code. |
197 | | std::conditional_t<std::is_trivially_destructible<T>::value, DirectImpl, |
198 | | PlacementImpl> |
199 | | impl_; |
200 | | }; |
201 | | |
202 | | #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION |
203 | | // Provide 'Class Template Argument Deduction': the type of NoDestructor's T |
204 | | // will be the same type as the argument passed to NoDestructor's constructor. |
205 | | template <typename T> |
206 | | NoDestructor(T) -> NoDestructor<T>; |
207 | | #endif // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION |
208 | | |
209 | | ABSL_NAMESPACE_END |
210 | | } // namespace absl |
211 | | |
212 | | #endif // ABSL_BASE_NO_DESTRUCTOR_H_ |