diff options
-rw-r--r-- | base/base.gyp | 2 | ||||
-rw-r--r-- | base/weak_ptr.h | 221 | ||||
-rw-r--r-- | base/weak_ptr_unittest.cc | 122 |
3 files changed, 345 insertions, 0 deletions
diff --git a/base/base.gyp b/base/base.gyp index 3e0503e..6636b53 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -339,6 +339,7 @@ 'waitable_event_win.cc', 'watchdog.cc', 'watchdog.h', + 'weak_ptr.h', 'win_util.cc', 'win_util.h', 'windows_message_list.h', @@ -643,6 +644,7 @@ 'waitable_event_unittest.cc', 'waitable_event_watcher_unittest.cc', 'watchdog_unittest.cc', + 'weak_ptr_unittest.cc', 'win_util_unittest.cc', 'wmi_util_unittest.cc', 'word_iterator_unittest.cc', diff --git a/base/weak_ptr.h b/base/weak_ptr.h new file mode 100644 index 0000000..bd7069a --- /dev/null +++ b/base/weak_ptr.h @@ -0,0 +1,221 @@ +// Copyright (c) 2009 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. + +// Weak pointers help in cases where you have many objects referring back to a +// shared object and you wish for the lifetime of the shared object to not be +// bound to the lifetime of the referrers. In other words, this is useful when +// reference counting is not a good fit. +// +// A common alternative to weak pointers is to have the shared object hold a +// list of all referrers, and then when the shared object is destroyed, it +// calls a method on the referrers to tell them to drop their references. This +// approach also requires the referrers to tell the shared object when they get +// destroyed so that the shared object can remove the referrer from its list of +// referrers. Such a solution works, but it is a bit complex. +// +// EXAMPLE: +// +// class Controller : public SupportsWeakPtr { +// public: +// void SpawnWorker() { Worker::StartNew(GetWeakPtr()); } +// void WorkComplete(const Result& result) { ... } +// }; +// +// class Worker { +// public: +// static void StartNew(const WeakPtr<Controller>& controller) { +// Worker* worker = new Worker(controller); +// // Kick off asynchronous processing... +// } +// private: +// Worker(const WeakPtr<Controller>& controller) +// : controller_(controller) {} +// void DidCompleteAsynchronousProcessing(const Result& result) { +// if (controller_) +// controller_->WorkComplete(result); +// } +// WeakPtr<Controller> controller_; +// }; +// +// Given the above classes, a consumer may allocate a Controller object, call +// SpawnWorker several times, and then destroy the Controller object before all +// of the workers have completed. Because the Worker class only holds a weak +// pointer to the Controller, we don't have to worry about the Worker +// dereferencing the Controller back pointer after the Controller has been +// destroyed. +// +// WARNING: weak pointers are not threadsafe!!! You must only use a WeakPtr +// instance on thread where it was created. + +#ifndef BASE_WEAK_PTR_H_ +#define BASE_WEAK_PTR_H_ + +#include "base/logging.h" +#include "base/non_thread_safe.h" +#include "base/ref_counted.h" + +namespace base { + +namespace internal { +// These classes are part of the WeakPtr implementation. +// DO NOT USE THESE CLASSES DIRECTLY YOURSELF. + +class WeakReference { + public: + void EnsureInitialized() { + // Lazy initialization helps faciliate the NonThreadSafe debug checks. + if (!flag_) { + flag_ = new Flag(); + flag_->data = true; + } + } + + void Invalidate() { + if (flag_) + flag_->data = false; + } + + bool is_valid() const { return flag_ && flag_->data; } + + private: + // A reference counted boolean that is true when the weak reference is valid + // and false otherwise. + class Flag : public RefCountedData<bool>, public NonThreadSafe { + }; + + scoped_refptr<Flag> flag_; +}; + +class WeakReferenceOwner { + public: + ~WeakReferenceOwner() { + ref_.Invalidate(); + } + + const WeakReference& GetRef() const { + ref_.EnsureInitialized(); + return ref_; + } + + void Invalidate() { ref_.Invalidate(); } + + private: + mutable WeakReference ref_; +}; + +// This class simplifies the implementation of WeakPtr's type conversion +// constructor by avoiding the need for a public accessor for ref_. A +// WeakPtr<T> cannot access the private members of WeakPtr<U>, so this +// base class gives us a way to access ref_ in a protected fashion. +class WeakPtrBase { + public: + WeakPtrBase() { + } + + protected: + WeakPtrBase(const WeakReference& ref) : ref_(ref) { + } + + WeakReference ref_; +}; + +} // namespace internal + +template <typename T> class SupportsWeakPtr; +template <typename T> class WeakPtrFactory; + +// The WeakPtr class holds a weak reference to |T*|. +// +// This class is designed to be used like a normal pointer. You should always +// null-test an object of this class before using it or invoking a method that +// may result in the underlying object being destroyed. +// +// EXAMPLE: +// +// class Foo { ... }; +// WeakPtr<Foo> foo; +// if (foo) +// foo->method(); +// +template <typename T> +class WeakPtr : public internal::WeakPtrBase { + public: + WeakPtr() : ptr_(NULL) { + } + + // Allow conversion from U to T provided U "is a" T. + template <typename U> + WeakPtr(const WeakPtr<U>& other) : WeakPtrBase(other), ptr_(other.get()) { + } + + T* get() const { return ref_.is_valid() ? ptr_ : NULL; } + operator T*() const { return get(); } + + T* operator*() const { + DCHECK(get() != NULL); + return *get(); + } + T* operator->() const { + DCHECK(get() != NULL); + return get(); + } + + private: + friend class SupportsWeakPtr<T>; + friend class WeakPtrFactory<T>; + + WeakPtr(const internal::WeakReference& ref, T* ptr) + : WeakPtrBase(ref), ptr_(ptr) { + } + + // This pointer is only valid when ref_.is_valid() is true. Otherwise, its + // value is undefined (as opposed to NULL). + T* ptr_; +}; + +// A class may extend from SupportsWeakPtr to expose weak pointers to itself. +// This is useful in cases where you want others to be able to get a weak +// pointer to your class. It also has the property that you don't need to +// initialize it from your constructor. +template <class T> +class SupportsWeakPtr { + public: + SupportsWeakPtr() {} + + WeakPtr<T> AsWeakPtr() { + return WeakPtr<T>(weak_reference_owner_.GetRef(), static_cast<T*>(this)); + } + + private: + internal::WeakReferenceOwner weak_reference_owner_; + DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr); +}; + +// A class may alternatively be composed of a WeakPtrFactory and thereby +// control how it exposes weak pointers to itself. This is helpful if you only +// need weak pointers within the implementation of a class. This class is also +// useful when working with primitive types. For example, you could have a +// WeakPtrFactory<bool> that is used to pass around a weak reference to a bool. +template <class T> +class WeakPtrFactory { + public: + explicit WeakPtrFactory(T* ptr) : ptr_(ptr) { + } + + WeakPtr<T> GetWeakPtr() { + return WeakPtr<T>(weak_reference_owner_.GetRef(), ptr_); + } + + // Call this method to invalidate all existing weak pointers. + void InvalidateWeakPtrs() { weak_reference_owner_.Invalidate(); } + + private: + internal::WeakReferenceOwner weak_reference_owner_; + T* ptr_; + DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory); +}; + +} // namespace base + +#endif // BASE_WEAK_PTR_H_ diff --git a/base/weak_ptr_unittest.cc b/base/weak_ptr_unittest.cc new file mode 100644 index 0000000..80c277b --- /dev/null +++ b/base/weak_ptr_unittest.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2009 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 "testing/gtest/include/gtest/gtest.h" +#include "base/thread.h" +#include "base/weak_ptr.h" + +namespace base { +namespace { + +template <class T> +class OffThreadObjectCreator { + public: + static T* NewObject() { + T* result; + { + Thread creator_thread("creator_thread"); + creator_thread.Start(); + creator_thread.message_loop()->PostTask(FROM_HERE, + NewRunnableFunction(OffThreadObjectCreator::CreateObject, &result)); + } + DCHECK(result); // We synchronized on thread destruction above. + return result; + } + private: + static void CreateObject(T** result) { + *result = new T; + } +}; + +struct Base {}; +struct Derived : Base {}; + +struct Producer : SupportsWeakPtr<Producer> {}; +struct Consumer { WeakPtr<Producer> producer; }; + +} // namespace + +TEST(WeakPtrTest, Basic) { + int data; + WeakPtrFactory<int> factory(&data); + WeakPtr<int> ptr = factory.GetWeakPtr(); + EXPECT_EQ(&data, ptr.get()); +} + +TEST(WeakPtrTest, Comparison) { + int data; + WeakPtrFactory<int> factory(&data); + WeakPtr<int> ptr = factory.GetWeakPtr(); + WeakPtr<int> ptr2 = ptr; + EXPECT_TRUE(ptr == ptr2); +} + +TEST(WeakPtrTest, OutOfScope) { + WeakPtr<int> ptr; + EXPECT_EQ(NULL, ptr.get()); + { + int data; + WeakPtrFactory<int> factory(&data); + ptr = factory.GetWeakPtr(); + } + EXPECT_EQ(NULL, ptr.get()); +} + +TEST(WeakPtrTest, Multiple) { + WeakPtr<int> a, b; + { + int data; + WeakPtrFactory<int> factory(&data); + a = factory.GetWeakPtr(); + b = factory.GetWeakPtr(); + EXPECT_EQ(&data, a.get()); + EXPECT_EQ(&data, b.get()); + } + EXPECT_EQ(NULL, a.get()); + EXPECT_EQ(NULL, b.get()); +} + +TEST(WeakPtrTest, UpCast) { + Derived data; + WeakPtrFactory<Derived> factory(&data); + WeakPtr<Base> ptr = factory.GetWeakPtr(); + ptr = factory.GetWeakPtr(); + EXPECT_EQ(ptr.get(), &data); +} + +TEST(WeakPtrTest, SupportsWeakPtr) { + Producer f; + WeakPtr<Producer> ptr = f.AsWeakPtr(); + EXPECT_EQ(&f, ptr.get()); +} + +TEST(WeakPtrTest, InvalidateWeakPtrs) { + int data; + WeakPtrFactory<int> factory(&data); + WeakPtr<int> ptr = factory.GetWeakPtr(); + EXPECT_EQ(&data, ptr.get()); + factory.InvalidateWeakPtrs(); + EXPECT_EQ(NULL, ptr.get()); +} + +TEST(WeakPtrTest, SingleThreaded1) { + // Test that it is OK to create a class that supports weak references on one + // thread, but use it on another. This tests that we do not trip runtime + // checks that ensure that a weak reference is not used by multiple threads. + scoped_ptr<Producer> producer(OffThreadObjectCreator<Producer>::NewObject()); + WeakPtr<Producer> weak_producer = producer->AsWeakPtr(); + EXPECT_EQ(producer.get(), weak_producer.get()); +} + +TEST(WeakPtrTest, SingleThreaded2) { + // Test that it is OK to create a class that has a WeakPtr member on one + // thread, but use it on another. This tests that we do not trip runtime + // checks that ensure that a weak reference is not used by multiple threads. + scoped_ptr<Consumer> consumer(OffThreadObjectCreator<Consumer>::NewObject()); + Producer producer; + consumer->producer = producer.AsWeakPtr(); + EXPECT_EQ(&producer, consumer->producer.get()); +} + +} // namespace base |