Coverage Report

Created: 2024-09-23 06:29

/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_