summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/SConscript2
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/lazy_instance.cc34
-rw-r--r--base/lazy_instance.h105
-rw-r--r--base/lazy_instance_unittest.cc100
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);
+ }
+}