// 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 "base/at_exit.h" #include "base/atomic_sequence_num.h" #include "base/lazy_instance.h" #include "base/memory/aligned_memory.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace { base::StaticAtomicSequenceNumber constructed_seq_; base::StaticAtomicSequenceNumber destructed_seq_; class ConstructAndDestructLogger { public: ConstructAndDestructLogger() { constructed_seq_.GetNext(); } ~ConstructAndDestructLogger() { destructed_seq_.GetNext(); } }; 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_; }; int SlowConstructor::constructed = 0; class SlowDelegate : public base::DelegateSimpleThread::Delegate { public: explicit SlowDelegate(base::LazyInstance<SlowConstructor>* lazy) : lazy_(lazy) {} void Run() override { EXPECT_EQ(12, lazy_->Get().some_int()); EXPECT_EQ(12, lazy_->Pointer()->some_int()); } private: base::LazyInstance<SlowConstructor>* lazy_; }; } // namespace static base::LazyInstance<ConstructAndDestructLogger> lazy_logger = LAZY_INSTANCE_INITIALIZER; TEST(LazyInstanceTest, Basic) { { base::ShadowingAtExitManager shadow; EXPECT_EQ(0, constructed_seq_.GetNext()); EXPECT_EQ(0, destructed_seq_.GetNext()); lazy_logger.Get(); EXPECT_EQ(2, constructed_seq_.GetNext()); EXPECT_EQ(1, destructed_seq_.GetNext()); lazy_logger.Pointer(); EXPECT_EQ(3, constructed_seq_.GetNext()); EXPECT_EQ(2, destructed_seq_.GetNext()); } EXPECT_EQ(4, constructed_seq_.GetNext()); EXPECT_EQ(4, destructed_seq_.GetNext()); } static base::LazyInstance<SlowConstructor> 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_(NULL) {} ~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> 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() {} ~AlignedData() {} base::AlignedMemory<alignment, alignment> data_; }; } // anonymous 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> > align4 = LAZY_INSTANCE_INITIALIZER; static LazyInstance<AlignedData<32> > align32 = LAZY_INSTANCE_INITIALIZER; static LazyInstance<AlignedData<4096> > align4096 = LAZY_INSTANCE_INITIALIZER; EXPECT_ALIGNED(align4.Pointer(), 4); EXPECT_ALIGNED(align32.Pointer(), 32); EXPECT_ALIGNED(align4096.Pointer(), 4096); }