diff options
-rw-r--r-- | base/SConscript | 2 | ||||
-rw-r--r-- | base/build/base.vcproj | 8 | ||||
-rw-r--r-- | base/build/base_unittests.vcproj | 4 | ||||
-rw-r--r-- | base/lazy_instance.cc | 34 | ||||
-rw-r--r-- | base/lazy_instance.h | 105 | ||||
-rw-r--r-- | base/lazy_instance_unittest.cc | 100 |
6 files changed, 253 insertions, 0 deletions
diff --git a/base/SConscript b/base/SConscript index c76423d..2a19517 100644 --- a/base/SConscript +++ b/base/SConscript @@ -40,6 +40,7 @@ input_files = [ 'icu_util.cc', 'json_reader.cc', 'json_writer.cc', + 'lazy_instance.cc', 'lock.cc', 'logging.cc', 'md5.cc', @@ -233,6 +234,7 @@ test_files = [ 'histogram_unittest.cc', 'json_reader_unittest.cc', 'json_writer_unittest.cc', + 'lazy_instance_unittest.cc', 'linked_ptr_unittest.cc', 'message_loop_unittest.cc', 'observer_list_unittest.cc', diff --git a/base/build/base.vcproj b/base/build/base.vcproj index 27d7e3c..2dd71a1 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -370,6 +370,14 @@ > </File> <File + RelativePath="..\lazy_instance.cc" + > + </File> + <File + RelativePath="..\lazy_instance.h" + > + </File> + <File RelativePath="..\linked_ptr.h" > </File> diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj index dbfd6a4..f3993da 100644 --- a/base/build/base_unittests.vcproj +++ b/base/build/base_unittests.vcproj @@ -212,6 +212,10 @@ > </File> <File + RelativePath="..\lazy_instance_unittest.cc" + > + </File> + <File RelativePath="..\linked_ptr_unittest.cc" > </File> diff --git a/base/lazy_instance.cc b/base/lazy_instance.cc new file mode 100644 index 0000000..3f089e0 --- /dev/null +++ b/base/lazy_instance.cc @@ -0,0 +1,34 @@ +// 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/lazy_instance.h" + +#include "base/at_exit.h" +#include "base/atomicops.h" +#include "base/basictypes.h" +#include "base/platform_thread.h" + +namespace base { + +void LazyInstanceHelper::EnsureInstance(void* instance, + void (*ctor)(void*), + void (*dtor)(void*)) { + // Try to create the instance, if we're the first, will go from EMPTY + // to CREATING, otherwise we've already been beaten here. + if (base::subtle::Acquire_CompareAndSwap( + &state_, STATE_EMPTY, STATE_CREATING) == STATE_EMPTY) { + // Created the instance in the space provided by |instance|. + ctor(instance); + // Instance is created, go from CREATING to CREATED. + base::subtle::Release_Store(&state_, STATE_CREATED); + // Register the destructor callback with AtExitManager. + base::AtExitManager::RegisterCallback(dtor, instance); + } else { + // It's either in the process of being created, or already created. Spin. + while (base::subtle::NoBarrier_Load(&state_) != STATE_CREATED) + PlatformThread::YieldCurrentThread(); + } +} + +} // namespace base diff --git a/base/lazy_instance.h b/base/lazy_instance.h new file mode 100644 index 0000000..5be41bd --- /dev/null +++ b/base/lazy_instance.h @@ -0,0 +1,105 @@ +// 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. + +// The LazyInstance<Type, Traits> class manages a single instance of Type, +// which will be lazily created on the first time it's accessed. This class is +// useful for places you would normally use a function-level static, but you +// need to have guaranteed thread-safety. The Type constructor will only ever +// be called once, even if two threads are racing to create the object. Get() +// and Pointer() will always return the same, completely initialized instance. +// When the instance is constructed it is registered with AtExitManager. The +// destructor will be called on program exit. +// +// LazyInstance is completely thread safe, assuming that you create it safely. +// The class was designed to be POD initialized, so it shouldn't require a +// static constructor. It really only makes sense to declare a LazyInstance as +// a global variable using the base::LinkerInitialized constructor. +// +// LazyInstance is similar to Singleton, except it does not have the singleton +// property. You can have multiple LazyInstance's of the same type, and each +// will manage a unique instance. It also preallocates the space for Type, as +// to avoid allocating the Type instance on the heap. This may help with the +// performance of creating the instance, and reducing heap fragmentation. This +// requires that Type be a complete type so we can determine the size. +// +// Example usage: +// static LazyInstance<MyClass> my_instance(base::LINKER_INITALIZED); +// void SomeMethod() { +// my_instance.Get().SomeMethod(); // MyClass::SomeMethod() +// +// MyClass* ptr = my_instance.Pointer(); +// ptr->DoDoDo(); // MyClass::DoDoDo +// } + +#ifndef BASE_LAZY_INSTANCE_H_ +#define BASE_LAZY_INSTANCE_H_ + +#include "base/atomicops.h" +#include "base/basictypes.h" + +namespace base { + +template <typename Type> +struct DefaultLazyInstanceTraits { + static void New(void* instance) { + // Use placement new to initialize our instance in our preallocated space. + // The parenthesis is very important here to force POD type initialization. + new (instance) Type(); + } + static void Delete(void* instance) { + // Explicitly call the destructor. + reinterpret_cast<Type*>(instance)->~Type(); + } +}; + +// We pull out some of the functionality into a non-templated base, so that we +// can implement the more complicated pieces out of line in the .cc file. +class LazyInstanceHelper { + protected: + enum { + STATE_EMPTY = 0, + STATE_CREATING = 1, + STATE_CREATED = 2 + }; + + explicit LazyInstanceHelper(LinkerInitialized x) { /* state_ is 0 */ } + ~LazyInstanceHelper() { } + + // Make sure that instance is created, creating or waiting for it to be + // created if neccessary. Constructs with |ctor| in the space provided by + // |instance| and registers dtor for destruction at program exit. + void EnsureInstance(void* instance, void (*ctor)(void*), void (*dtor)(void*)); + + base::subtle::Atomic32 state_; +}; + +template <typename Type, typename Traits = DefaultLazyInstanceTraits<Type> > +class LazyInstance : public LazyInstanceHelper { + public: + explicit LazyInstance(LinkerInitialized x) : LazyInstanceHelper(x) { } + ~LazyInstance() { } + + Type& Get() { + return *Pointer(); + } + + Type* Pointer() { + Type* instance = reinterpret_cast<Type*>(&buf_); + + // We will hopefully have fast access when the instance is already created. + if (base::subtle::NoBarrier_Load(&state_) != STATE_CREATED) + EnsureInstance(instance, Traits::New, Traits::Delete); + + return instance; + } + + private: + int8 buf_[sizeof(Type)]; // Preallocate the space for the Type instance. + + DISALLOW_COPY_AND_ASSIGN(LazyInstance); +}; + +} // namespace base + +#endif // BASE_LAZY_INSTANCE_H_ diff --git a/base/lazy_instance_unittest.cc b/base/lazy_instance_unittest.cc new file mode 100644 index 0000000..f447723 --- /dev/null +++ b/base/lazy_instance_unittest.cc @@ -0,0 +1,100 @@ +// 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/simple_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ShadowingAtExitManager : public base::AtExitManager { + public: + ShadowingAtExitManager() : AtExitManager(true) { } +}; + +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) { + PlatformThread::Sleep(1000); // Sleep for 1 second to try to cause a race. + ++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: + 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) { + { + 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) { + { + 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); + } +} |