• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 
7 #include <memory>
8 #include <vector>
9 
10 #include "base/at_exit.h"
11 #include "base/atomic_sequence_num.h"
12 #include "base/atomicops.h"
13 #include "base/barrier_closure.h"
14 #include "base/bind.h"
15 #include "base/lazy_instance.h"
16 #include "base/sys_info.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/threading/simple_thread.h"
19 #include "base/time/time.h"
20 #include "build/build_config.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 namespace {
24 
25 base::AtomicSequenceNumber constructed_seq_;
26 base::AtomicSequenceNumber destructed_seq_;
27 
28 class ConstructAndDestructLogger {
29  public:
ConstructAndDestructLogger()30   ConstructAndDestructLogger() {
31     constructed_seq_.GetNext();
32   }
~ConstructAndDestructLogger()33   ~ConstructAndDestructLogger() {
34     destructed_seq_.GetNext();
35   }
36 
37  private:
38   DISALLOW_COPY_AND_ASSIGN(ConstructAndDestructLogger);
39 };
40 
41 class SlowConstructor {
42  public:
SlowConstructor()43   SlowConstructor() : some_int_(0) {
44     // Sleep for 1 second to try to cause a race.
45     base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
46     ++constructed;
47     some_int_ = 12;
48   }
some_int() const49   int some_int() const { return some_int_; }
50 
51   static int constructed;
52  private:
53   int some_int_;
54 
55   DISALLOW_COPY_AND_ASSIGN(SlowConstructor);
56 };
57 
58 // static
59 int SlowConstructor::constructed = 0;
60 
61 class SlowDelegate : public base::DelegateSimpleThread::Delegate {
62  public:
SlowDelegate(base::LazyInstance<SlowConstructor>::DestructorAtExit * lazy)63   explicit SlowDelegate(
64       base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy)
65       : lazy_(lazy) {}
66 
Run()67   void Run() override {
68     EXPECT_EQ(12, lazy_->Get().some_int());
69     EXPECT_EQ(12, lazy_->Pointer()->some_int());
70   }
71 
72  private:
73   base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy_;
74 
75   DISALLOW_COPY_AND_ASSIGN(SlowDelegate);
76 };
77 
78 }  // namespace
79 
80 base::LazyInstance<ConstructAndDestructLogger>::DestructorAtExit lazy_logger =
81     LAZY_INSTANCE_INITIALIZER;
82 
TEST(LazyInstanceTest,Basic)83 TEST(LazyInstanceTest, Basic) {
84   {
85     base::ShadowingAtExitManager shadow;
86 
87     EXPECT_FALSE(lazy_logger.IsCreated());
88     EXPECT_EQ(0, constructed_seq_.GetNext());
89     EXPECT_EQ(0, destructed_seq_.GetNext());
90 
91     lazy_logger.Get();
92     EXPECT_TRUE(lazy_logger.IsCreated());
93     EXPECT_EQ(2, constructed_seq_.GetNext());
94     EXPECT_EQ(1, destructed_seq_.GetNext());
95 
96     lazy_logger.Pointer();
97     EXPECT_TRUE(lazy_logger.IsCreated());
98     EXPECT_EQ(3, constructed_seq_.GetNext());
99     EXPECT_EQ(2, destructed_seq_.GetNext());
100   }
101   EXPECT_FALSE(lazy_logger.IsCreated());
102   EXPECT_EQ(4, constructed_seq_.GetNext());
103   EXPECT_EQ(4, destructed_seq_.GetNext());
104 }
105 
106 base::LazyInstance<SlowConstructor>::DestructorAtExit lazy_slow =
107     LAZY_INSTANCE_INITIALIZER;
108 
TEST(LazyInstanceTest,ConstructorThreadSafety)109 TEST(LazyInstanceTest, ConstructorThreadSafety) {
110   {
111     base::ShadowingAtExitManager shadow;
112 
113     SlowDelegate delegate(&lazy_slow);
114     EXPECT_EQ(0, SlowConstructor::constructed);
115 
116     base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5);
117     pool.AddWork(&delegate, 20);
118     EXPECT_EQ(0, SlowConstructor::constructed);
119 
120     pool.Start();
121     pool.JoinAll();
122     EXPECT_EQ(1, SlowConstructor::constructed);
123   }
124 }
125 
126 namespace {
127 
128 // DeleteLogger is an object which sets a flag when it's destroyed.
129 // It accepts a bool* and sets the bool to true when the dtor runs.
130 class DeleteLogger {
131  public:
DeleteLogger()132   DeleteLogger() : deleted_(nullptr) {}
~DeleteLogger()133   ~DeleteLogger() { *deleted_ = true; }
134 
SetDeletedPtr(bool * deleted)135   void SetDeletedPtr(bool* deleted) {
136     deleted_ = deleted;
137   }
138 
139  private:
140   bool* deleted_;
141 };
142 
143 }  // anonymous namespace
144 
TEST(LazyInstanceTest,LeakyLazyInstance)145 TEST(LazyInstanceTest, LeakyLazyInstance) {
146   // Check that using a plain LazyInstance causes the dtor to run
147   // when the AtExitManager finishes.
148   bool deleted1 = false;
149   {
150     base::ShadowingAtExitManager shadow;
151     static base::LazyInstance<DeleteLogger>::DestructorAtExit test =
152         LAZY_INSTANCE_INITIALIZER;
153     test.Get().SetDeletedPtr(&deleted1);
154   }
155   EXPECT_TRUE(deleted1);
156 
157   // Check that using a *leaky* LazyInstance makes the dtor not run
158   // when the AtExitManager finishes.
159   bool deleted2 = false;
160   {
161     base::ShadowingAtExitManager shadow;
162     static base::LazyInstance<DeleteLogger>::Leaky
163         test = LAZY_INSTANCE_INITIALIZER;
164     test.Get().SetDeletedPtr(&deleted2);
165   }
166   EXPECT_FALSE(deleted2);
167 }
168 
169 namespace {
170 
171 template <size_t alignment>
172 class AlignedData {
173  public:
174   AlignedData() = default;
175   ~AlignedData() = default;
176   alignas(alignment) char data_[alignment];
177 };
178 
179 }  // namespace
180 
181 #define EXPECT_ALIGNED(ptr, align) \
182     EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
183 
TEST(LazyInstanceTest,Alignment)184 TEST(LazyInstanceTest, Alignment) {
185   using base::LazyInstance;
186 
187   // Create some static instances with increasing sizes and alignment
188   // requirements. By ordering this way, the linker will need to do some work to
189   // ensure proper alignment of the static data.
190   static LazyInstance<AlignedData<4>>::DestructorAtExit align4 =
191       LAZY_INSTANCE_INITIALIZER;
192   static LazyInstance<AlignedData<32>>::DestructorAtExit align32 =
193       LAZY_INSTANCE_INITIALIZER;
194   static LazyInstance<AlignedData<4096>>::DestructorAtExit align4096 =
195       LAZY_INSTANCE_INITIALIZER;
196 
197   EXPECT_ALIGNED(align4.Pointer(), 4);
198   EXPECT_ALIGNED(align32.Pointer(), 32);
199   EXPECT_ALIGNED(align4096.Pointer(), 4096);
200 }
201 
202 namespace {
203 
204 // A class whose constructor busy-loops until it is told to complete
205 // construction.
206 class BlockingConstructor {
207  public:
BlockingConstructor()208   BlockingConstructor() {
209     EXPECT_FALSE(WasConstructorCalled());
210     base::subtle::NoBarrier_Store(&constructor_called_, 1);
211     EXPECT_TRUE(WasConstructorCalled());
212     while (!base::subtle::NoBarrier_Load(&complete_construction_))
213       base::PlatformThread::YieldCurrentThread();
214     done_construction_ = true;
215   }
216 
~BlockingConstructor()217   ~BlockingConstructor() {
218     // Restore static state for the next test.
219     base::subtle::NoBarrier_Store(&constructor_called_, 0);
220     base::subtle::NoBarrier_Store(&complete_construction_, 0);
221   }
222 
223   // Returns true if BlockingConstructor() was entered.
WasConstructorCalled()224   static bool WasConstructorCalled() {
225     return base::subtle::NoBarrier_Load(&constructor_called_);
226   }
227 
228   // Instructs BlockingConstructor() that it may now unblock its construction.
CompleteConstructionNow()229   static void CompleteConstructionNow() {
230     base::subtle::NoBarrier_Store(&complete_construction_, 1);
231   }
232 
done_construction()233   bool done_construction() { return done_construction_; }
234 
235  private:
236   // Use Atomic32 instead of AtomicFlag for them to be trivially initialized.
237   static base::subtle::Atomic32 constructor_called_;
238   static base::subtle::Atomic32 complete_construction_;
239 
240   bool done_construction_ = false;
241 
242   DISALLOW_COPY_AND_ASSIGN(BlockingConstructor);
243 };
244 
245 // A SimpleThread running at |thread_priority| which invokes |before_get|
246 // (optional) and then invokes Get() on the LazyInstance it's assigned.
247 class BlockingConstructorThread : public base::SimpleThread {
248  public:
BlockingConstructorThread(base::ThreadPriority thread_priority,base::LazyInstance<BlockingConstructor>::DestructorAtExit * lazy,base::OnceClosure before_get)249   BlockingConstructorThread(
250       base::ThreadPriority thread_priority,
251       base::LazyInstance<BlockingConstructor>::DestructorAtExit* lazy,
252       base::OnceClosure before_get)
253       : SimpleThread("BlockingConstructorThread", Options(thread_priority)),
254         lazy_(lazy),
255         before_get_(std::move(before_get)) {}
256 
Run()257   void Run() override {
258     if (before_get_)
259       std::move(before_get_).Run();
260     EXPECT_TRUE(lazy_->Get().done_construction());
261   }
262 
263  private:
264   base::LazyInstance<BlockingConstructor>::DestructorAtExit* lazy_;
265   base::OnceClosure before_get_;
266 
267   DISALLOW_COPY_AND_ASSIGN(BlockingConstructorThread);
268 };
269 
270 // static
271 base::subtle::Atomic32 BlockingConstructor::constructor_called_ = 0;
272 // static
273 base::subtle::Atomic32 BlockingConstructor::complete_construction_ = 0;
274 
275 base::LazyInstance<BlockingConstructor>::DestructorAtExit lazy_blocking =
276     LAZY_INSTANCE_INITIALIZER;
277 
278 }  // namespace
279 
280 // Tests that if the thread assigned to construct the LazyInstance runs at
281 // background priority : the foreground threads will yield to it enough for it
282 // to eventually complete construction.
283 // This is a regression test for https://crbug.com/797129.
TEST(LazyInstanceTest,PriorityInversionAtInitializationResolves)284 TEST(LazyInstanceTest, PriorityInversionAtInitializationResolves) {
285   base::TimeTicks test_begin = base::TimeTicks::Now();
286 
287   // Construct BlockingConstructor from a background thread.
288   BlockingConstructorThread background_getter(
289       base::ThreadPriority::BACKGROUND, &lazy_blocking, base::OnceClosure());
290   background_getter.Start();
291 
292   while (!BlockingConstructor::WasConstructorCalled())
293     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
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.
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));
304   for (int i = 0; i < kNumForegroundThreads; ++i) {
305     foreground_threads.push_back(std::make_unique<BlockingConstructorThread>(
306         base::ThreadPriority::NORMAL, &lazy_blocking,
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.
314   for (auto& foreground_thread : foreground_threads)
315     foreground_thread->Join();
316   background_getter.Join();
317 
318   // Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a
319   // Z840 without r527445 but is expected to be fast (~30ms) with the fix).
320   EXPECT_LT(base::TimeTicks::Now() - test_begin,
321             base::TimeDelta::FromSeconds(5));
322 }
323