Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 5 | #include "base/lazy_instance.h" |
| 6 | |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 9 | #include <atomic> |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 10 | #include <memory> |
Lei Zhang | 25c9ff6 | 2020-06-19 02:30:43 | [diff] [blame] | 11 | #include <utility> |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 12 | #include <vector> |
| 13 | |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 14 | #include "base/at_exit.h" |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 15 | #include "base/atomic_sequence_num.h" |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 16 | #include "base/barrier_closure.h" |
Avi Drissman | 63e1f99 | 2023-01-13 18:54:43 | [diff] [blame] | 17 | #include "base/functional/bind.h" |
| 18 | #include "base/functional/callback.h" |
Lei Zhang | 25c9ff6 | 2020-06-19 02:30:43 | [diff] [blame] | 19 | #include "base/memory/aligned_memory.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 20 | #include "base/memory/raw_ptr.h" |
Sebastien Marchand | 75a7cdf | 2018-11-13 23:47:03 | [diff] [blame] | 21 | #include "base/system/sys_info.h" |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 22 | #include "base/threading/platform_thread.h" |
[email protected] | ac9ba8fe | 2010-12-30 18:08:36 | [diff] [blame] | 23 | #include "base/threading/simple_thread.h" |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 24 | #include "base/time/time.h" |
brettw | 16289b3e | 2017-06-13 21:58:40 | [diff] [blame] | 25 | #include "build/build_config.h" |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 26 | #include "testing/gtest/include/gtest/gtest.h" |
| 27 | |
Daniel Cheng | 1b97e93 | 2018-01-23 23:51:31 | [diff] [blame] | 28 | namespace { |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 29 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 30 | base::AtomicSequenceNumber constructed_seq_; |
| 31 | base::AtomicSequenceNumber destructed_seq_; |
| 32 | |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 33 | class ConstructAndDestructLogger { |
| 34 | public: |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 35 | ConstructAndDestructLogger() { constructed_seq_.GetNext(); } |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 36 | ConstructAndDestructLogger(const ConstructAndDestructLogger&) = delete; |
| 37 | ConstructAndDestructLogger& operator=(const ConstructAndDestructLogger&) = |
| 38 | delete; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 39 | ~ConstructAndDestructLogger() { destructed_seq_.GetNext(); } |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 40 | }; |
| 41 | |
| 42 | class SlowConstructor { |
| 43 | public: |
Peter Kasting | 811504a7 | 2025-01-09 03:18:50 | [diff] [blame] | 44 | SlowConstructor() { |
[email protected] | ce072a7 | 2010-12-31 20:02:16 | [diff] [blame] | 45 | // Sleep for 1 second to try to cause a race. |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 46 | base::PlatformThread::Sleep(base::Seconds(1)); |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 47 | ++constructed; |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 48 | some_int_ = 12; |
| 49 | } |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 50 | SlowConstructor(const SlowConstructor&) = delete; |
| 51 | SlowConstructor& operator=(const SlowConstructor&) = delete; |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 52 | int some_int() const { return some_int_; } |
| 53 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 54 | static int constructed; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 55 | |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 56 | private: |
Peter Kasting | 811504a7 | 2025-01-09 03:18:50 | [diff] [blame] | 57 | int some_int_ = 0; |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 58 | }; |
| 59 | |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 60 | // static |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 61 | int SlowConstructor::constructed = 0; |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 62 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 63 | class SlowDelegate : public base::DelegateSimpleThread::Delegate { |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 64 | public: |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 65 | explicit SlowDelegate( |
| 66 | base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy) |
[email protected] | 2fdc86a | 2010-01-26 23:08:02 | [diff] [blame] | 67 | : lazy_(lazy) {} |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 68 | SlowDelegate(const SlowDelegate&) = delete; |
| 69 | SlowDelegate& operator=(const SlowDelegate&) = delete; |
[email protected] | 2fdc86a | 2010-01-26 23:08:02 | [diff] [blame] | 70 | |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 71 | void Run() override { |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 72 | EXPECT_EQ(12, lazy_->Get().some_int()); |
| 73 | EXPECT_EQ(12, lazy_->Pointer()->some_int()); |
| 74 | } |
| 75 | |
| 76 | private: |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 77 | raw_ptr<base::LazyInstance<SlowConstructor>::DestructorAtExit> lazy_; |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 78 | }; |
| 79 | |
| 80 | } // namespace |
| 81 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 82 | base::LazyInstance<ConstructAndDestructLogger>::DestructorAtExit lazy_logger = |
| 83 | LAZY_INSTANCE_INITIALIZER; |
| 84 | |
| 85 | TEST(LazyInstanceTest, Basic) { |
| 86 | { |
| 87 | base::ShadowingAtExitManager shadow; |
| 88 | |
| 89 | EXPECT_FALSE(lazy_logger.IsCreated()); |
| 90 | EXPECT_EQ(0, constructed_seq_.GetNext()); |
| 91 | EXPECT_EQ(0, destructed_seq_.GetNext()); |
| 92 | |
| 93 | lazy_logger.Get(); |
| 94 | EXPECT_TRUE(lazy_logger.IsCreated()); |
| 95 | EXPECT_EQ(2, constructed_seq_.GetNext()); |
| 96 | EXPECT_EQ(1, destructed_seq_.GetNext()); |
| 97 | |
| 98 | lazy_logger.Pointer(); |
| 99 | EXPECT_TRUE(lazy_logger.IsCreated()); |
| 100 | EXPECT_EQ(3, constructed_seq_.GetNext()); |
| 101 | EXPECT_EQ(2, destructed_seq_.GetNext()); |
| 102 | } |
| 103 | EXPECT_FALSE(lazy_logger.IsCreated()); |
| 104 | EXPECT_EQ(4, constructed_seq_.GetNext()); |
| 105 | EXPECT_EQ(4, destructed_seq_.GetNext()); |
| 106 | } |
| 107 | |
| 108 | base::LazyInstance<SlowConstructor>::DestructorAtExit lazy_slow = |
| 109 | LAZY_INSTANCE_INITIALIZER; |
| 110 | |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 111 | TEST(LazyInstanceTest, ConstructorThreadSafety) { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 112 | { |
| 113 | base::ShadowingAtExitManager shadow; |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 114 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 115 | SlowDelegate delegate(&lazy_slow); |
| 116 | EXPECT_EQ(0, SlowConstructor::constructed); |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 117 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 118 | base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5); |
| 119 | pool.AddWork(&delegate, 20); |
| 120 | EXPECT_EQ(0, SlowConstructor::constructed); |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 121 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 122 | pool.Start(); |
| 123 | pool.JoinAll(); |
| 124 | EXPECT_EQ(1, SlowConstructor::constructed); |
| 125 | } |
[email protected] | 30039e6 | 2008-09-08 14:11:13 | [diff] [blame] | 126 | } |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 127 | |
| 128 | namespace { |
| 129 | |
| 130 | // DeleteLogger is an object which sets a flag when it's destroyed. |
| 131 | // It accepts a bool* and sets the bool to true when the dtor runs. |
| 132 | class DeleteLogger { |
| 133 | public: |
Ivan Kotenkov | a16212a5 | 2017-11-08 12:37:33 | [diff] [blame] | 134 | DeleteLogger() : deleted_(nullptr) {} |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 135 | ~DeleteLogger() { *deleted_ = true; } |
| 136 | |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 137 | void SetDeletedPtr(bool* deleted) { deleted_ = deleted; } |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 138 | |
| 139 | private: |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 140 | raw_ptr<bool> deleted_; |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 141 | }; |
| 142 | |
| 143 | } // anonymous namespace |
| 144 | |
| 145 | TEST(LazyInstanceTest, LeakyLazyInstance) { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 146 | // Check that using a plain LazyInstance causes the dtor to run |
| 147 | // when the AtExitManager finishes. |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 148 | bool deleted1 = false; |
| 149 | { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 150 | base::ShadowingAtExitManager shadow; |
| 151 | static base::LazyInstance<DeleteLogger>::DestructorAtExit test = |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 152 | LAZY_INSTANCE_INITIALIZER; |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 153 | test.Get().SetDeletedPtr(&deleted1); |
| 154 | } |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 155 | EXPECT_TRUE(deleted1); |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 156 | |
| 157 | // Check that using a *leaky* LazyInstance makes the dtor not run |
| 158 | // when the AtExitManager finishes. |
| 159 | bool deleted2 = false; |
| 160 | { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 161 | base::ShadowingAtExitManager shadow; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 162 | static base::LazyInstance<DeleteLogger>::Leaky test = |
| 163 | LAZY_INSTANCE_INITIALIZER; |
[email protected] | dcc6933 | 2010-10-21 20:41:47 | [diff] [blame] | 164 | test.Get().SetDeletedPtr(&deleted2); |
| 165 | } |
| 166 | EXPECT_FALSE(deleted2); |
| 167 | } |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 168 | |
| 169 | namespace { |
| 170 | |
| 171 | template <size_t alignment> |
| 172 | class AlignedData { |
| 173 | public: |
Chris Watkins | bb7211c | 2017-11-29 07:16:38 | [diff] [blame] | 174 | AlignedData() = default; |
| 175 | ~AlignedData() = default; |
brettw | 16289b3e | 2017-06-13 21:58:40 | [diff] [blame] | 176 | alignas(alignment) char data_[alignment]; |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 177 | }; |
| 178 | |
brettw | 16289b3e | 2017-06-13 21:58:40 | [diff] [blame] | 179 | } // namespace |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 180 | |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 181 | TEST(LazyInstanceTest, Alignment) { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 182 | using base::LazyInstance; |
| 183 | |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 184 | // Create some static instances with increasing sizes and alignment |
| 185 | // requirements. By ordering this way, the linker will need to do some work to |
| 186 | // ensure proper alignment of the static data. |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 187 | static LazyInstance<AlignedData<4>>::DestructorAtExit align4 = |
| 188 | LAZY_INSTANCE_INITIALIZER; |
| 189 | static LazyInstance<AlignedData<32>>::DestructorAtExit align32 = |
| 190 | LAZY_INSTANCE_INITIALIZER; |
| 191 | static LazyInstance<AlignedData<4096>>::DestructorAtExit align4096 = |
| 192 | LAZY_INSTANCE_INITIALIZER; |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 193 | |
Lei Zhang | 25c9ff6 | 2020-06-19 02:30:43 | [diff] [blame] | 194 | EXPECT_TRUE(base::IsAligned(align4.Pointer(), 4)); |
| 195 | EXPECT_TRUE(base::IsAligned(align32.Pointer(), 32)); |
| 196 | EXPECT_TRUE(base::IsAligned(align4096.Pointer(), 4096)); |
[email protected] | cd924d6 | 2012-02-23 17:52:20 | [diff] [blame] | 197 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 198 | |
| 199 | namespace { |
| 200 | |
| 201 | // A class whose constructor busy-loops until it is told to complete |
| 202 | // construction. |
| 203 | class BlockingConstructor { |
| 204 | public: |
| 205 | BlockingConstructor() { |
| 206 | EXPECT_FALSE(WasConstructorCalled()); |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 207 | constructor_called_.store(true, std::memory_order_relaxed); |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 208 | EXPECT_TRUE(WasConstructorCalled()); |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 209 | while (!complete_construction_.load(std::memory_order_relaxed)) { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 210 | base::PlatformThread::YieldCurrentThread(); |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 211 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 212 | done_construction_ = true; |
| 213 | } |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 214 | BlockingConstructor(const BlockingConstructor&) = delete; |
| 215 | BlockingConstructor& operator=(const BlockingConstructor&) = delete; |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 216 | ~BlockingConstructor() { |
| 217 | // Restore static state for the next test. |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 218 | constructor_called_.store(false, std::memory_order_relaxed); |
| 219 | complete_construction_.store(false, std::memory_order_relaxed); |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 220 | } |
| 221 | |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 222 | // Returns true if BlockingConstructor() was entered. |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 223 | static bool WasConstructorCalled() { |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 224 | return constructor_called_.load(std::memory_order_relaxed); |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 225 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 226 | |
| 227 | // Instructs BlockingConstructor() that it may now unblock its construction. |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 228 | static void CompleteConstructionNow() { |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 229 | complete_construction_.store(true, std::memory_order_relaxed); |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 230 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 231 | |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 232 | bool done_construction() const { return done_construction_; } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 233 | |
| 234 | private: |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 235 | // Use Atomic32 instead of AtomicFlag for them to be trivially initialized. |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 236 | static std::atomic<bool> constructor_called_; |
| 237 | static std::atomic<bool> complete_construction_; |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 238 | |
| 239 | bool done_construction_ = false; |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 240 | }; |
| 241 | |
Zhibo Wang | d9e4a00 | 2022-07-07 04:34:59 | [diff] [blame] | 242 | // A SimpleThread running at |thread_type| which invokes |before_get| (optional) |
| 243 | // and then invokes Get() on the LazyInstance it's assigned. |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 244 | class BlockingConstructorThread : public base::SimpleThread { |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 245 | public: |
| 246 | BlockingConstructorThread( |
Zhibo Wang | d9e4a00 | 2022-07-07 04:34:59 | [diff] [blame] | 247 | base::ThreadType thread_type, |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 248 | base::LazyInstance<BlockingConstructor>::DestructorAtExit* lazy, |
| 249 | base::OnceClosure before_get) |
Zhibo Wang | d9e4a00 | 2022-07-07 04:34:59 | [diff] [blame] | 250 | : SimpleThread("BlockingConstructorThread", Options(thread_type)), |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 251 | lazy_(lazy), |
| 252 | before_get_(std::move(before_get)) {} |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 253 | BlockingConstructorThread(const BlockingConstructorThread&) = delete; |
| 254 | BlockingConstructorThread& operator=(const BlockingConstructorThread&) = |
| 255 | delete; |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 256 | |
| 257 | void Run() override { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 258 | if (before_get_) { |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 259 | std::move(before_get_).Run(); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 260 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 261 | EXPECT_TRUE(lazy_->Get().done_construction()); |
| 262 | } |
| 263 | |
| 264 | private: |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 265 | raw_ptr<base::LazyInstance<BlockingConstructor>::DestructorAtExit> lazy_; |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 266 | base::OnceClosure before_get_; |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 267 | }; |
| 268 | |
| 269 | // static |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 270 | std::atomic<bool> BlockingConstructor::constructor_called_ = false; |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 271 | // static |
Benoît Lizé | aac3718 | 2024-12-09 12:42:24 | [diff] [blame] | 272 | std::atomic<bool> BlockingConstructor::complete_construction_ = false; |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 273 | |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 274 | base::LazyInstance<BlockingConstructor>::DestructorAtExit lazy_blocking = |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 275 | LAZY_INSTANCE_INITIALIZER; |
| 276 | |
| 277 | } // namespace |
| 278 | |
| 279 | // Tests that if the thread assigned to construct the LazyInstance runs at |
| 280 | // background priority : the foreground threads will yield to it enough for it |
| 281 | // to eventually complete construction. |
| 282 | // This is a regression test for https://crbug.com/797129. |
| 283 | TEST(LazyInstanceTest, PriorityInversionAtInitializationResolves) { |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 284 | base::TimeTicks test_begin = base::TimeTicks::Now(); |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 285 | |
| 286 | // Construct BlockingConstructor from a background thread. |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 287 | BlockingConstructorThread background_getter( |
Zhibo Wang | d9e4a00 | 2022-07-07 04:34:59 | [diff] [blame] | 288 | base::ThreadType::kBackground, &lazy_blocking, base::OnceClosure()); |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 289 | background_getter.Start(); |
| 290 | |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 291 | while (!BlockingConstructor::WasConstructorCalled()) { |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 292 | base::PlatformThread::Sleep(base::Milliseconds(1)); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 293 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 294 | |
| 295 | // Spin 4 foreground thread per core contending to get the already under |
| 296 | // construction LazyInstance. When they are all running and poking at it : |
| 297 | // allow the background thread to complete its work. |
Joe Downing | 0d4683d | 2018-01-24 01:32:38 | [diff] [blame] | 298 | const int kNumForegroundThreads = 4 * base::SysInfo::NumberOfProcessors(); |
| 299 | std::vector<std::unique_ptr<base::SimpleThread>> foreground_threads; |
| 300 | base::RepeatingClosure foreground_thread_ready_callback = |
| 301 | base::BarrierClosure( |
| 302 | kNumForegroundThreads, |
| 303 | base::BindOnce(&BlockingConstructor::CompleteConstructionNow)); |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 304 | for (int i = 0; i < kNumForegroundThreads; ++i) { |
| 305 | foreground_threads.push_back(std::make_unique<BlockingConstructorThread>( |
Zhibo Wang | d9e4a00 | 2022-07-07 04:34:59 | [diff] [blame] | 306 | base::ThreadType::kDefault, &lazy_blocking, |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 307 | foreground_thread_ready_callback)); |
| 308 | foreground_threads.back()->Start(); |
| 309 | } |
| 310 | |
| 311 | // This test will hang if the foreground threads become stuck in |
| 312 | // LazyInstance::Get() per the background thread never being scheduled to |
| 313 | // complete construction. |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 314 | for (auto& foreground_thread : foreground_threads) { |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 315 | foreground_thread->Join(); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 316 | } |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 317 | background_getter.Join(); |
| 318 | |
| 319 | // Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a |
| 320 | // Z840 without r527445 but is expected to be fast (~30ms) with the fix). |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 321 | EXPECT_LT(base::TimeTicks::Now() - test_begin, base::Seconds(5)); |
Gabriel Charette | 70b37302 | 2018-01-17 21:00:18 | [diff] [blame] | 322 | } |