blob: a9c165e62606e162860711acca0303813ded515e [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#include "base/no_destructor.h"
6
Benoît Lizéaac37182024-12-09 12:42:247#include <atomic>
David Bienvenub4b441e2020-09-23 05:49:578#include <memory>
Daniel Cheng73999fe62018-01-19 00:28:159#include <string>
10#include <utility>
David Bienvenub4b441e2020-09-23 05:49:5711#include <vector>
Daniel Cheng73999fe62018-01-19 00:28:1512
Gabriel Charette773e8562019-01-25 22:03:4013#include "base/barrier_closure.h"
Hans Wennborgc3cffa62020-04-27 10:09:1214#include "base/check.h"
Avi Drissman63e1f992023-01-13 18:54:4315#include "base/functional/bind.h"
Peter Boström54119652024-11-14 00:16:3816#include "base/notreached.h"
Gabriel Charette773e8562019-01-25 22:03:4017#include "base/system/sys_info.h"
18#include "base/threading/platform_thread.h"
19#include "base/threading/simple_thread.h"
Jose Dapena Paz4b136a92018-03-19 20:11:2020#include "build/build_config.h"
Daniel Cheng73999fe62018-01-19 00:28:1521#include "testing/gtest/include/gtest/gtest.h"
22
23namespace base {
24
25namespace {
26
Daniel Cheng117c6a92022-10-10 23:47:2827static_assert(!std::is_trivially_destructible_v<std::string>);
28static_assert(
29 std::is_trivially_destructible_v<base::NoDestructor<std::string>>);
30
Peter Boström54119652024-11-14 00:16:3831struct NotreachedOnDestroy {
32 ~NotreachedOnDestroy() { NOTREACHED(); }
Daniel Cheng73999fe62018-01-19 00:28:1533};
34
35TEST(NoDestructorTest, SkipsDestructors) {
Peter Boström54119652024-11-14 00:16:3836 NoDestructor<NotreachedOnDestroy> destructor_should_not_run;
Daniel Cheng73999fe62018-01-19 00:28:1537}
38
Daniel Cheng13fe006d2020-10-29 07:33:1539struct UncopyableUnmovable {
40 UncopyableUnmovable() = default;
41 explicit UncopyableUnmovable(int value) : value(value) {}
42
43 UncopyableUnmovable(const UncopyableUnmovable&) = delete;
44 UncopyableUnmovable& operator=(const UncopyableUnmovable&) = delete;
45
46 int value = 1;
Avi Drissmanded77172021-07-02 18:23:0047 std::string something_with_a_nontrivial_destructor;
Daniel Cheng13fe006d2020-10-29 07:33:1548};
49
Daniel Cheng73999fe62018-01-19 00:28:1550struct CopyOnly {
51 CopyOnly() = default;
52
53 CopyOnly(const CopyOnly&) = default;
54 CopyOnly& operator=(const CopyOnly&) = default;
55
56 CopyOnly(CopyOnly&&) = delete;
57 CopyOnly& operator=(CopyOnly&&) = delete;
58};
59
60struct MoveOnly {
61 MoveOnly() = default;
62
63 MoveOnly(const MoveOnly&) = delete;
64 MoveOnly& operator=(const MoveOnly&) = delete;
65
66 MoveOnly(MoveOnly&&) = default;
67 MoveOnly& operator=(MoveOnly&&) = default;
68};
69
70struct ForwardingTestStruct {
71 ForwardingTestStruct(const CopyOnly&, MoveOnly&&) {}
Avi Drissmanded77172021-07-02 18:23:0072
73 std::string something_with_a_nontrivial_destructor;
Daniel Cheng73999fe62018-01-19 00:28:1574};
75
Daniel Cheng13fe006d2020-10-29 07:33:1576TEST(NoDestructorTest, UncopyableUnmovable) {
77 static NoDestructor<UncopyableUnmovable> default_constructed;
78 EXPECT_EQ(1, default_constructed->value);
79
80 static NoDestructor<UncopyableUnmovable> constructed_with_arg(-1);
81 EXPECT_EQ(-1, constructed_with_arg->value);
82}
83
Daniel Cheng73999fe62018-01-19 00:28:1584TEST(NoDestructorTest, ForwardsArguments) {
85 CopyOnly copy_only;
86 MoveOnly move_only;
87
88 static NoDestructor<ForwardingTestStruct> test_forwarding(
89 copy_only, std::move(move_only));
90}
91
92TEST(NoDestructorTest, Accessors) {
93 static NoDestructor<std::string> awesome("awesome");
94
95 EXPECT_EQ("awesome", *awesome);
96 EXPECT_EQ(0, awesome->compare("awesome"));
97 EXPECT_EQ(0, awesome.get()->compare("awesome"));
98}
99
Jose Dapena Paz4b136a92018-03-19 20:11:20100// Passing initializer list to a NoDestructor like in this test
101// is ambiguous in GCC.
102// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84849
103#if !defined(COMPILER_GCC) && !defined(__clang__)
Daniel Chengbae24732018-02-14 19:42:14104TEST(NoDestructorTest, InitializerList) {
105 static NoDestructor<std::vector<std::string>> vector({"a", "b", "c"});
106}
Jose Dapena Paz4b136a92018-03-19 20:11:20107#endif
Daniel Cheng73999fe62018-01-19 00:28:15108} // namespace
109
Gabriel Charette773e8562019-01-25 22:03:40110namespace {
111
112// A class whose constructor busy-loops until it is told to complete
113// construction.
114class BlockingConstructor {
115 public:
116 BlockingConstructor() {
117 EXPECT_FALSE(WasConstructorCalled());
Benoît Lizéaac37182024-12-09 12:42:24118 constructor_called_.store(true, std::memory_order_relaxed);
Gabriel Charette773e8562019-01-25 22:03:40119 EXPECT_TRUE(WasConstructorCalled());
Benoît Lizéaac37182024-12-09 12:42:24120 while (!complete_construction_.load(std::memory_order_relaxed)) {
Gabriel Charette773e8562019-01-25 22:03:40121 PlatformThread::YieldCurrentThread();
Benoît Lizéaac37182024-12-09 12:42:24122 }
Gabriel Charette773e8562019-01-25 22:03:40123 done_construction_ = true;
124 }
David Bienvenub4b441e2020-09-23 05:49:57125 BlockingConstructor(const BlockingConstructor&) = delete;
126 BlockingConstructor& operator=(const BlockingConstructor&) = delete;
Gabriel Charette773e8562019-01-25 22:03:40127 ~BlockingConstructor() = delete;
128
129 // Returns true if BlockingConstructor() was entered.
130 static bool WasConstructorCalled() {
Benoît Lizéaac37182024-12-09 12:42:24131 return constructor_called_.load(std::memory_order_relaxed);
Gabriel Charette773e8562019-01-25 22:03:40132 }
133
134 // Instructs BlockingConstructor() that it may now unblock its construction.
135 static void CompleteConstructionNow() {
Benoît Lizéaac37182024-12-09 12:42:24136 complete_construction_.store(true, std::memory_order_relaxed);
Gabriel Charette773e8562019-01-25 22:03:40137 }
138
David Bienvenub4b441e2020-09-23 05:49:57139 bool done_construction() const { return done_construction_; }
Gabriel Charette773e8562019-01-25 22:03:40140
141 private:
Benoît Lizéaac37182024-12-09 12:42:24142 static std::atomic<bool> constructor_called_;
143 static std::atomic<bool> complete_construction_;
Gabriel Charette773e8562019-01-25 22:03:40144
145 bool done_construction_ = false;
Gabriel Charette773e8562019-01-25 22:03:40146};
147
148// static
Benoît Lizéaac37182024-12-09 12:42:24149std::atomic<bool> BlockingConstructor::constructor_called_ = false;
Gabriel Charette773e8562019-01-25 22:03:40150// static
Benoît Lizéaac37182024-12-09 12:42:24151std::atomic<bool> BlockingConstructor::complete_construction_ = false;
Gabriel Charette773e8562019-01-25 22:03:40152
Zhibo Wangd9e4a002022-07-07 04:34:59153// A SimpleThread running at |thread_type| which invokes |before_get| (optional)
154// and then invokes thread-safe scoped-static-initializationconstruction on its
155// NoDestructor instance.
Gabriel Charette773e8562019-01-25 22:03:40156class BlockingConstructorThread : public SimpleThread {
157 public:
Zhibo Wangd9e4a002022-07-07 04:34:59158 BlockingConstructorThread(ThreadType thread_type, OnceClosure before_get)
159 : SimpleThread("BlockingConstructorThread", Options(thread_type)),
Gabriel Charette773e8562019-01-25 22:03:40160 before_get_(std::move(before_get)) {}
David Bienvenub4b441e2020-09-23 05:49:57161 BlockingConstructorThread(const BlockingConstructorThread&) = delete;
162 BlockingConstructorThread& operator=(const BlockingConstructorThread&) =
163 delete;
Gabriel Charette773e8562019-01-25 22:03:40164
165 void Run() override {
Peter Kasting134ef9af2024-12-28 02:30:09166 if (before_get_) {
Gabriel Charette773e8562019-01-25 22:03:40167 std::move(before_get_).Run();
Peter Kasting134ef9af2024-12-28 02:30:09168 }
Gabriel Charette773e8562019-01-25 22:03:40169
170 static NoDestructor<BlockingConstructor> instance;
171 EXPECT_TRUE(instance->done_construction());
172 }
173
174 private:
175 OnceClosure before_get_;
Gabriel Charette773e8562019-01-25 22:03:40176};
177
178} // namespace
179
180// Tests that if the thread assigned to construct the local-static
181// initialization of the NoDestructor runs at background priority : the
182// foreground threads will yield to it enough for it to eventually complete
183// construction. While local-static thread-safe initialization isn't specific to
184// NoDestructor, it is tested here as NoDestructor is set to replace
185// LazyInstance and this is an important regression test for it
186// (https://crbug.com/797129).
187TEST(NoDestructorTest, PriorityInversionAtStaticInitializationResolves) {
188 TimeTicks test_begin = TimeTicks::Now();
189
Bruce Dawson232b9162022-01-20 17:37:23190 // Construct BlockingConstructor from a thread that is lower priority than the
191 // other threads that will be constructed. This thread used to be BACKGROUND
192 // priority but that caused it to be starved by other simultaneously running
193 // test processes, leading to false-positive failures.
Zhibo Wangd9e4a002022-07-07 04:34:59194 BlockingConstructorThread background_getter(ThreadType::kDefault,
Gabriel Charette773e8562019-01-25 22:03:40195 OnceClosure());
196 background_getter.Start();
197
Peter Kasting134ef9af2024-12-28 02:30:09198 while (!BlockingConstructor::WasConstructorCalled()) {
Peter Kasting53fd6ee2021-10-05 20:40:48199 PlatformThread::Sleep(Milliseconds(1));
Peter Kasting134ef9af2024-12-28 02:30:09200 }
Gabriel Charette773e8562019-01-25 22:03:40201
202 // Spin 4 foreground thread per core contending to get the already under
203 // construction NoDestructor. When they are all running and poking at it :
204 // allow the background thread to complete its work.
205 const int kNumForegroundThreads = 4 * SysInfo::NumberOfProcessors();
206 std::vector<std::unique_ptr<SimpleThread>> foreground_threads;
207 RepeatingClosure foreground_thread_ready_callback =
208 BarrierClosure(kNumForegroundThreads,
209 BindOnce(&BlockingConstructor::CompleteConstructionNow));
210 for (int i = 0; i < kNumForegroundThreads; ++i) {
Bruce Dawson232b9162022-01-20 17:37:23211 // Create threads that are higher priority than background_getter. See above
212 // for why these particular priorities are chosen.
Gabriel Charette773e8562019-01-25 22:03:40213 foreground_threads.push_back(std::make_unique<BlockingConstructorThread>(
Zhibo Wangd9e4a002022-07-07 04:34:59214 ThreadType::kDisplayCritical, foreground_thread_ready_callback));
Gabriel Charette773e8562019-01-25 22:03:40215 foreground_threads.back()->Start();
216 }
217
218 // This test will hang if the foreground threads become stuck in
219 // NoDestructor's construction per the background thread never being scheduled
220 // to complete construction.
Peter Kasting134ef9af2024-12-28 02:30:09221 for (auto& foreground_thread : foreground_threads) {
Gabriel Charette773e8562019-01-25 22:03:40222 foreground_thread->Join();
Peter Kasting134ef9af2024-12-28 02:30:09223 }
Gabriel Charette773e8562019-01-25 22:03:40224 background_getter.Join();
225
226 // Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a
Bruce Dawson232b9162022-01-20 17:37:23227 // Z840 without https://crrev.com/527445 but is expected to be fast (~30ms)
228 // with the fix).
Peter Kasting53fd6ee2021-10-05 20:40:48229 EXPECT_LT(TimeTicks::Now() - test_begin, Seconds(5));
Gabriel Charette773e8562019-01-25 22:03:40230}
231
Daniel Cheng73999fe62018-01-19 00:28:15232} // namespace base