// Copyright (c) 2008 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 "base/at_exit.h" #include "base/atomic_sequence_num.h" #include "base/lazy_instance.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace { base::AtomicSequenceNumber constructed_seq_(base::LINKER_INITIALIZED); base::AtomicSequenceNumber destructed_seq_(base::LINKER_INITIALIZED); 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(1000); ++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) {} virtual void Run() { 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( base::LINKER_INITIALIZED); 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(base::LINKER_INITIALIZED); 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(base::LINKER_INITIALIZED); 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, base::LeakyLazyInstanceTraits<DeleteLogger> > test(base::LINKER_INITIALIZED); test.Get().SetDeletedPtr(&deleted2); } EXPECT_FALSE(deleted2); }