|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/at_exit.h" | 
|  | #include "base/atomic_sequence_num.h" | 
|  | #include "base/atomicops.h" | 
|  | #include "base/barrier_closure.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/sys_info.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/threading/simple_thread.h" | 
|  | #include "base/time/time.h" | 
|  | #include "build_config.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | base::AtomicSequenceNumber constructed_seq_; | 
|  | base::AtomicSequenceNumber destructed_seq_; | 
|  |  | 
|  | class ConstructAndDestructLogger { | 
|  | public: | 
|  | ConstructAndDestructLogger() { | 
|  | constructed_seq_.GetNext(); | 
|  | } | 
|  | ~ConstructAndDestructLogger() { | 
|  | destructed_seq_.GetNext(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(ConstructAndDestructLogger); | 
|  | }; | 
|  |  | 
|  | class SlowConstructor { | 
|  | public: | 
|  | SlowConstructor() : some_int_(0) { | 
|  | // Sleep for 1 second to try to cause a race. | 
|  | base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); | 
|  | ++constructed; | 
|  | some_int_ = 12; | 
|  | } | 
|  | int some_int() const { return some_int_; } | 
|  |  | 
|  | static int constructed; | 
|  | private: | 
|  | int some_int_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(SlowConstructor); | 
|  | }; | 
|  |  | 
|  | // static | 
|  | int SlowConstructor::constructed = 0; | 
|  |  | 
|  | class SlowDelegate : public base::DelegateSimpleThread::Delegate { | 
|  | public: | 
|  | explicit SlowDelegate( | 
|  | base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy) | 
|  | : lazy_(lazy) {} | 
|  |  | 
|  | void Run() override { | 
|  | EXPECT_EQ(12, lazy_->Get().some_int()); | 
|  | EXPECT_EQ(12, lazy_->Pointer()->some_int()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | base::LazyInstance<SlowConstructor>::DestructorAtExit* lazy_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(SlowDelegate); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | base::LazyInstance<ConstructAndDestructLogger>::DestructorAtExit lazy_logger = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | TEST(LazyInstanceTest, Basic) { | 
|  | { | 
|  | base::ShadowingAtExitManager shadow; | 
|  |  | 
|  | EXPECT_FALSE(lazy_logger.IsCreated()); | 
|  | EXPECT_EQ(0, constructed_seq_.GetNext()); | 
|  | EXPECT_EQ(0, destructed_seq_.GetNext()); | 
|  |  | 
|  | lazy_logger.Get(); | 
|  | EXPECT_TRUE(lazy_logger.IsCreated()); | 
|  | EXPECT_EQ(2, constructed_seq_.GetNext()); | 
|  | EXPECT_EQ(1, destructed_seq_.GetNext()); | 
|  |  | 
|  | lazy_logger.Pointer(); | 
|  | EXPECT_TRUE(lazy_logger.IsCreated()); | 
|  | EXPECT_EQ(3, constructed_seq_.GetNext()); | 
|  | EXPECT_EQ(2, destructed_seq_.GetNext()); | 
|  | } | 
|  | EXPECT_FALSE(lazy_logger.IsCreated()); | 
|  | EXPECT_EQ(4, constructed_seq_.GetNext()); | 
|  | EXPECT_EQ(4, destructed_seq_.GetNext()); | 
|  | } | 
|  |  | 
|  | base::LazyInstance<SlowConstructor>::DestructorAtExit lazy_slow = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | TEST(LazyInstanceTest, ConstructorThreadSafety) { | 
|  | { | 
|  | base::ShadowingAtExitManager shadow; | 
|  |  | 
|  | SlowDelegate delegate(&lazy_slow); | 
|  | EXPECT_EQ(0, SlowConstructor::constructed); | 
|  |  | 
|  | base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5); | 
|  | pool.AddWork(&delegate, 20); | 
|  | EXPECT_EQ(0, SlowConstructor::constructed); | 
|  |  | 
|  | pool.Start(); | 
|  | pool.JoinAll(); | 
|  | EXPECT_EQ(1, SlowConstructor::constructed); | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // DeleteLogger is an object which sets a flag when it's destroyed. | 
|  | // It accepts a bool* and sets the bool to true when the dtor runs. | 
|  | class DeleteLogger { | 
|  | public: | 
|  | DeleteLogger() : deleted_(nullptr) {} | 
|  | ~DeleteLogger() { *deleted_ = true; } | 
|  |  | 
|  | void SetDeletedPtr(bool* deleted) { | 
|  | deleted_ = deleted; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool* deleted_; | 
|  | }; | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | TEST(LazyInstanceTest, LeakyLazyInstance) { | 
|  | // Check that using a plain LazyInstance causes the dtor to run | 
|  | // when the AtExitManager finishes. | 
|  | bool deleted1 = false; | 
|  | { | 
|  | base::ShadowingAtExitManager shadow; | 
|  | static base::LazyInstance<DeleteLogger>::DestructorAtExit test = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  | test.Get().SetDeletedPtr(&deleted1); | 
|  | } | 
|  | EXPECT_TRUE(deleted1); | 
|  |  | 
|  | // Check that using a *leaky* LazyInstance makes the dtor not run | 
|  | // when the AtExitManager finishes. | 
|  | bool deleted2 = false; | 
|  | { | 
|  | base::ShadowingAtExitManager shadow; | 
|  | static base::LazyInstance<DeleteLogger>::Leaky | 
|  | test = LAZY_INSTANCE_INITIALIZER; | 
|  | test.Get().SetDeletedPtr(&deleted2); | 
|  | } | 
|  | EXPECT_FALSE(deleted2); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <size_t alignment> | 
|  | class AlignedData { | 
|  | public: | 
|  | AlignedData() = default; | 
|  | ~AlignedData() = default; | 
|  | alignas(alignment) char data_[alignment]; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #define EXPECT_ALIGNED(ptr, align) \ | 
|  | EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1)) | 
|  |  | 
|  | TEST(LazyInstanceTest, Alignment) { | 
|  | using base::LazyInstance; | 
|  |  | 
|  | // Create some static instances with increasing sizes and alignment | 
|  | // requirements. By ordering this way, the linker will need to do some work to | 
|  | // ensure proper alignment of the static data. | 
|  | static LazyInstance<AlignedData<4>>::DestructorAtExit align4 = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  | static LazyInstance<AlignedData<32>>::DestructorAtExit align32 = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  | static LazyInstance<AlignedData<4096>>::DestructorAtExit align4096 = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | EXPECT_ALIGNED(align4.Pointer(), 4); | 
|  | EXPECT_ALIGNED(align32.Pointer(), 32); | 
|  | EXPECT_ALIGNED(align4096.Pointer(), 4096); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // A class whose constructor busy-loops until it is told to complete | 
|  | // construction. | 
|  | class BlockingConstructor { | 
|  | public: | 
|  | BlockingConstructor() { | 
|  | EXPECT_FALSE(WasConstructorCalled()); | 
|  | base::subtle::NoBarrier_Store(&constructor_called_, 1); | 
|  | EXPECT_TRUE(WasConstructorCalled()); | 
|  | while (!base::subtle::NoBarrier_Load(&complete_construction_)) | 
|  | base::PlatformThread::YieldCurrentThread(); | 
|  | done_construction_ = true; | 
|  | } | 
|  |  | 
|  | ~BlockingConstructor() { | 
|  | // Restore static state for the next test. | 
|  | base::subtle::NoBarrier_Store(&constructor_called_, 0); | 
|  | base::subtle::NoBarrier_Store(&complete_construction_, 0); | 
|  | } | 
|  |  | 
|  | // Returns true if BlockingConstructor() was entered. | 
|  | static bool WasConstructorCalled() { | 
|  | return base::subtle::NoBarrier_Load(&constructor_called_); | 
|  | } | 
|  |  | 
|  | // Instructs BlockingConstructor() that it may now unblock its construction. | 
|  | static void CompleteConstructionNow() { | 
|  | base::subtle::NoBarrier_Store(&complete_construction_, 1); | 
|  | } | 
|  |  | 
|  | bool done_construction() { return done_construction_; } | 
|  |  | 
|  | private: | 
|  | // Use Atomic32 instead of AtomicFlag for them to be trivially initialized. | 
|  | static base::subtle::Atomic32 constructor_called_; | 
|  | static base::subtle::Atomic32 complete_construction_; | 
|  |  | 
|  | bool done_construction_ = false; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BlockingConstructor); | 
|  | }; | 
|  |  | 
|  | // A SimpleThread running at |thread_priority| which invokes |before_get| | 
|  | // (optional) and then invokes Get() on the LazyInstance it's assigned. | 
|  | class BlockingConstructorThread : public base::SimpleThread { | 
|  | public: | 
|  | BlockingConstructorThread( | 
|  | base::ThreadPriority thread_priority, | 
|  | base::LazyInstance<BlockingConstructor>::DestructorAtExit* lazy, | 
|  | base::OnceClosure before_get) | 
|  | : SimpleThread("BlockingConstructorThread", Options(thread_priority)), | 
|  | lazy_(lazy), | 
|  | before_get_(std::move(before_get)) {} | 
|  |  | 
|  | void Run() override { | 
|  | if (before_get_) | 
|  | std::move(before_get_).Run(); | 
|  | EXPECT_TRUE(lazy_->Get().done_construction()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | base::LazyInstance<BlockingConstructor>::DestructorAtExit* lazy_; | 
|  | base::OnceClosure before_get_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BlockingConstructorThread); | 
|  | }; | 
|  |  | 
|  | // static | 
|  | base::subtle::Atomic32 BlockingConstructor::constructor_called_ = 0; | 
|  | // static | 
|  | base::subtle::Atomic32 BlockingConstructor::complete_construction_ = 0; | 
|  |  | 
|  | base::LazyInstance<BlockingConstructor>::DestructorAtExit lazy_blocking = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Tests that if the thread assigned to construct the LazyInstance runs at | 
|  | // background priority : the foreground threads will yield to it enough for it | 
|  | // to eventually complete construction. | 
|  | // This is a regression test for https://crbug.com/797129. | 
|  | TEST(LazyInstanceTest, PriorityInversionAtInitializationResolves) { | 
|  | base::TimeTicks test_begin = base::TimeTicks::Now(); | 
|  |  | 
|  | // Construct BlockingConstructor from a background thread. | 
|  | BlockingConstructorThread background_getter( | 
|  | base::ThreadPriority::BACKGROUND, &lazy_blocking, base::OnceClosure()); | 
|  | background_getter.Start(); | 
|  |  | 
|  | while (!BlockingConstructor::WasConstructorCalled()) | 
|  | base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); | 
|  |  | 
|  | // Spin 4 foreground thread per core contending to get the already under | 
|  | // construction LazyInstance. When they are all running and poking at it : | 
|  | // allow the background thread to complete its work. | 
|  | const int kNumForegroundThreads = 4 * base::SysInfo::NumberOfProcessors(); | 
|  | std::vector<std::unique_ptr<base::SimpleThread>> foreground_threads; | 
|  | base::RepeatingClosure foreground_thread_ready_callback = | 
|  | base::BarrierClosure( | 
|  | kNumForegroundThreads, | 
|  | base::BindOnce(&BlockingConstructor::CompleteConstructionNow)); | 
|  | for (int i = 0; i < kNumForegroundThreads; ++i) { | 
|  | foreground_threads.push_back(std::make_unique<BlockingConstructorThread>( | 
|  | base::ThreadPriority::NORMAL, &lazy_blocking, | 
|  | foreground_thread_ready_callback)); | 
|  | foreground_threads.back()->Start(); | 
|  | } | 
|  |  | 
|  | // This test will hang if the foreground threads become stuck in | 
|  | // LazyInstance::Get() per the background thread never being scheduled to | 
|  | // complete construction. | 
|  | for (auto& foreground_thread : foreground_threads) | 
|  | foreground_thread->Join(); | 
|  | background_getter.Join(); | 
|  |  | 
|  | // Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a | 
|  | // Z840 without r527445 but is expected to be fast (~30ms) with the fix). | 
|  | EXPECT_LT(base::TimeTicks::Now() - test_begin, | 
|  | base::TimeDelta::FromSeconds(5)); | 
|  | } |