diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-11 21:39:03 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-11 21:39:03 +0000 |
commit | 5d6688f0761b93cc09ad883687a36b3a612b2c19 (patch) | |
tree | 4848308d121b45bcce196303cfe09481eb26546e /base/memory | |
parent | 2a0f1e8b7d4ff7cd68e6e3b0227e6129e2032a25 (diff) | |
download | chromium_src-5d6688f0761b93cc09ad883687a36b3a612b2c19.zip chromium_src-5d6688f0761b93cc09ad883687a36b3a612b2c19.tar.gz chromium_src-5d6688f0761b93cc09ad883687a36b3a612b2c19.tar.bz2 |
Second try of http://codereview.chromium.org/10698063/
Rename/re-comment tests and added new death tests for WeakPtr.
Review URL: https://chromiumcodereview.appspot.com/10690080
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146212 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/memory')
-rw-r--r-- | base/memory/weak_ptr.h | 33 | ||||
-rw-r--r-- | base/memory/weak_ptr_unittest.cc | 417 |
2 files changed, 292 insertions, 158 deletions
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h index c3a1e2e..7006fb6 100644 --- a/base/memory/weak_ptr.h +++ b/base/memory/weak_ptr.h @@ -7,15 +7,6 @@ // bound to the lifetime of the referrers. In other words, this is useful when // reference counting is not a good fit. // -// Thread-safety notes: -// When you get a WeakPtr (from a WeakPtrFactory or SupportsWeakPtr), -// the WeakPtr becomes bound to the current thread. You may only -// dereference the WeakPtr on that thread. However, it is safe to -// destroy the WeakPtr object on another thread. -// Since a WeakPtr object may be destroyed on a background thread, -// querying WeakPtrFactory's HasWeakPtrs() method can be racy. -// -// // 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 @@ -54,6 +45,27 @@ // dereferencing the Controller back pointer after the Controller has been // destroyed. // +// ------------------------ Thread-safety notes ------------------------ +// When you get a WeakPtr (from a WeakPtrFactory or SupportsWeakPtr), if it's +// the only one pointing to the object, the object become bound to the +// current thread, as well as this WeakPtr and all later ones get created. +// +// You may only dereference the WeakPtr on the thread it binds to. However, it +// is safe to destroy the WeakPtr object on another thread. Because of this, +// querying WeakPtrFactory's HasWeakPtrs() method can be racy. +// +// On the other hand, the object that supports WeakPtr (extends SupportsWeakPtr) +// can only be deleted from the thread it binds to, until all WeakPtrs are +// deleted. +// +// Calling SupportsWeakPtr::DetachFromThread() can work around the limitations +// above and cancel the thread binding of the object and all WeakPtrs pointing +// to it, but it's not recommended and unsafe. +// +// WeakPtrs may be copy-constructed or assigned on threads other than the thread +// they are bound to. This does not change the thread binding. So these WeakPtrs +// may only be dereferenced on the thread that the original WeakPtr was bound +// to. #ifndef BASE_MEMORY_WEAK_PTR_H_ #define BASE_MEMORY_WEAK_PTR_H_ @@ -224,7 +236,8 @@ class WeakPtr : public internal::WeakPtrBase { friend class WeakPtrFactory<T>; WeakPtr(const internal::WeakReference& ref, T* ptr) - : WeakPtrBase(ref), ptr_(ptr) { + : WeakPtrBase(ref), + ptr_(ptr) { } // This pointer is only valid when ref_.is_valid() is true. Otherwise, its diff --git a/base/memory/weak_ptr_unittest.cc b/base/memory/weak_ptr_unittest.cc index e18a207..d5f8057 100644 --- a/base/memory/weak_ptr_unittest.cc +++ b/base/memory/weak_ptr_unittest.cc @@ -37,12 +37,16 @@ class OffThreadObjectCreator { } }; -struct Base { std::string member; }; +struct Base { + std::string member; +}; struct Derived : Base {}; -struct Producer : SupportsWeakPtr<Producer> {}; -struct DerivedProducer : Producer {}; -struct Consumer { WeakPtr<Producer> producer; }; +struct Target : SupportsWeakPtr<Target> {}; +struct DerivedTarget : Target {}; +struct Arrow { + WeakPtr<Target> target; +}; // Helper class to create and destroy weak pointer copies // and delete objects on a background thread. @@ -54,80 +58,80 @@ class BackgroundThread : public Thread { Stop(); } - void CreateConsumerFromProducer(Consumer** consumer, Producer* producer) { + void CreateArrowFromTarget(Arrow** arrow, Target* target) { WaitableEvent completion(true, false); message_loop()->PostTask( FROM_HERE, - base::Bind(&BackgroundThread::DoCreateFromProducer, consumer, producer, - &completion)); + base::Bind(&BackgroundThread::DoCreateArrowFromTarget, + arrow, target, &completion)); completion.Wait(); } - void CreateConsumerFromConsumer(Consumer** consumer, const Consumer* other) { + void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) { WaitableEvent completion(true, false); message_loop()->PostTask( FROM_HERE, - base::Bind(&BackgroundThread::DoCreateFromConsumer, consumer, other, - &completion)); + base::Bind(&BackgroundThread::DoCreateArrowFromArrow, + arrow, other, &completion)); completion.Wait(); } - void DeleteProducer(Producer* object) { + void DeleteTarget(Target* object) { WaitableEvent completion(true, false); message_loop()->PostTask( FROM_HERE, - base::Bind(&BackgroundThread::DoDeleteProducer, object, &completion)); + base::Bind(&BackgroundThread::DoDeleteTarget, object, &completion)); completion.Wait(); } - void DeleteConsumer(Consumer* object) { + void DeleteArrow(Arrow* object) { WaitableEvent completion(true, false); message_loop()->PostTask( FROM_HERE, - base::Bind(&BackgroundThread::DoDeleteConsumer, object, &completion)); + base::Bind(&BackgroundThread::DoDeleteArrow, object, &completion)); completion.Wait(); } - Producer* DeRef(const Consumer* consumer) { + Target* DeRef(const Arrow* arrow) { WaitableEvent completion(true, false); - Producer* result = NULL; + Target* result = NULL; message_loop()->PostTask( FROM_HERE, - base::Bind(&BackgroundThread::DoDeRef, consumer, &result, &completion)); + base::Bind(&BackgroundThread::DoDeRef, arrow, &result, &completion)); completion.Wait(); return result; } protected: - static void DoCreateFromConsumer(Consumer** consumer, - const Consumer* other, - WaitableEvent* completion) { - *consumer = new Consumer; - **consumer = *other; + static void DoCreateArrowFromArrow(Arrow** arrow, + const Arrow* other, + WaitableEvent* completion) { + *arrow = new Arrow; + **arrow = *other; completion->Signal(); } - static void DoCreateFromProducer(Consumer** consumer, - Producer* producer, - WaitableEvent* completion) { - *consumer = new Consumer; - (*consumer)->producer = producer->AsWeakPtr(); + static void DoCreateArrowFromTarget(Arrow** arrow, + Target* target, + WaitableEvent* completion) { + *arrow = new Arrow; + (*arrow)->target = target->AsWeakPtr(); completion->Signal(); } - static void DoDeRef(const Consumer* consumer, - Producer** result, + static void DoDeRef(const Arrow* arrow, + Target** result, WaitableEvent* completion) { - *result = consumer->producer.get(); + *result = arrow->target.get(); completion->Signal(); } - static void DoDeleteProducer(Producer* object, WaitableEvent* completion) { + static void DoDeleteTarget(Target* object, WaitableEvent* completion) { delete object; completion->Signal(); } - static void DoDeleteConsumer(Consumer* object, WaitableEvent* completion) { + static void DoDeleteArrow(Arrow* object, WaitableEvent* completion) { delete object; completion->Signal(); } @@ -135,33 +139,33 @@ class BackgroundThread : public Thread { } // namespace -TEST(WeakPtrTest, Basic) { +TEST(WeakPtrFactoryTest, Basic) { int data; WeakPtrFactory<int> factory(&data); WeakPtr<int> ptr = factory.GetWeakPtr(); EXPECT_EQ(&data, ptr.get()); } -TEST(WeakPtrTest, Comparison) { +TEST(WeakPtrFactoryTest, Comparison) { int data; WeakPtrFactory<int> factory(&data); WeakPtr<int> ptr = factory.GetWeakPtr(); WeakPtr<int> ptr2 = ptr; - EXPECT_TRUE(ptr == ptr2); + EXPECT_EQ(ptr, ptr2); } -TEST(WeakPtrTest, OutOfScope) { +TEST(WeakPtrFactoryTest, OutOfScope) { WeakPtr<int> ptr; - EXPECT_TRUE(ptr.get() == NULL); + EXPECT_EQ(NULL, ptr.get()); { int data; WeakPtrFactory<int> factory(&data); ptr = factory.GetWeakPtr(); } - EXPECT_TRUE(ptr.get() == NULL); + EXPECT_EQ(NULL, ptr.get()); } -TEST(WeakPtrTest, Multiple) { +TEST(WeakPtrFactoryTest, Multiple) { WeakPtr<int> a, b; { int data; @@ -171,11 +175,11 @@ TEST(WeakPtrTest, Multiple) { EXPECT_EQ(&data, a.get()); EXPECT_EQ(&data, b.get()); } - EXPECT_TRUE(a.get() == NULL); - EXPECT_TRUE(b.get() == NULL); + EXPECT_EQ(NULL, a.get()); + EXPECT_EQ(NULL, b.get()); } -TEST(WeakPtrTest, MultipleStaged) { +TEST(WeakPtrFactoryTest, MultipleStaged) { WeakPtr<int> a; { int data; @@ -184,12 +188,22 @@ TEST(WeakPtrTest, MultipleStaged) { { WeakPtr<int> b = factory.GetWeakPtr(); } - EXPECT_TRUE(a.get() != NULL); + EXPECT_TRUE(NULL != a.get()); } - EXPECT_TRUE(a.get() == NULL); + EXPECT_EQ(NULL, a.get()); +} + +TEST(WeakPtrFactoryTest, Dereference) { + Base data; + data.member = "123456"; + WeakPtrFactory<Base> factory(&data); + WeakPtr<Base> ptr = factory.GetWeakPtr(); + EXPECT_EQ(&data, ptr.get()); + EXPECT_EQ(data.member, (*ptr).member); + EXPECT_EQ(data.member, ptr->member); } -TEST(WeakPtrTest, UpCast) { +TEST(WeakPtrFactoryTest, UpCast) { Derived data; WeakPtrFactory<Derived> factory(&data); WeakPtr<Base> ptr = factory.GetWeakPtr(); @@ -198,15 +212,15 @@ TEST(WeakPtrTest, UpCast) { } TEST(WeakPtrTest, SupportsWeakPtr) { - Producer f; - WeakPtr<Producer> ptr = f.AsWeakPtr(); - EXPECT_EQ(&f, ptr.get()); + Target target; + WeakPtr<Target> ptr = target.AsWeakPtr(); + EXPECT_EQ(&target, ptr.get()); } -TEST(WeakPtrTest, DerivedProducer) { - DerivedProducer f; - WeakPtr<DerivedProducer> ptr = AsWeakPtr(&f); - EXPECT_EQ(&f, ptr.get()); +TEST(WeakPtrTest, DerivedTarget) { + DerivedTarget target; + WeakPtr<DerivedTarget> ptr = AsWeakPtr(&target); + EXPECT_EQ(&target, ptr.get()); } TEST(WeakPtrTest, InvalidateWeakPtrs) { @@ -216,7 +230,7 @@ TEST(WeakPtrTest, InvalidateWeakPtrs) { EXPECT_EQ(&data, ptr.get()); EXPECT_TRUE(factory.HasWeakPtrs()); factory.InvalidateWeakPtrs(); - EXPECT_TRUE(ptr.get() == NULL); + EXPECT_EQ(NULL, ptr.get()); EXPECT_FALSE(factory.HasWeakPtrs()); } @@ -230,127 +244,234 @@ TEST(WeakPtrTest, HasWeakPtrs) { EXPECT_FALSE(factory.HasWeakPtrs()); } -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, ObjectAndWeakPtrOnDifferentThreads) { + // Test that it is OK to create an object that supports WeakPtr on one thread, + // but use it on another. This tests that we do not trip runtime checks that + // ensure that a WeakPtr is not used by multiple threads. + scoped_ptr<Target> target(OffThreadObjectCreator<Target>::NewObject()); + WeakPtr<Target> weak_ptr = target->AsWeakPtr(); + EXPECT_EQ(target.get(), weak_ptr.get()); } -TEST(WeakPtrTest, SingleThreaded2) { - // Test that it is OK to create a class that has a WeakPtr member on one +TEST(WeakPtrTest, WeakPtrInitiateAndUseOnDifferentThreads) { + // Test that it is OK to create an object 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()); + // checks that ensure that a WeakPtr is not used by multiple threads. + scoped_ptr<Arrow> arrow(OffThreadObjectCreator<Arrow>::NewObject()); + Target target; + arrow->target = target.AsWeakPtr(); + EXPECT_EQ(&target, arrow->target.get()); } -TEST(WeakPtrTest, MoveOwnershipImplicit) { - // Move object ownership to other thread by releasing all weak pointers - // on the original thread first. Establishing weak pointers on a different - // thread after previous pointers have been destroyed implicitly reattaches - // the thread checks. - // - Thread A creates object and weak pointer - // - Thread A deletes the weak pointer - // - Thread B creates weak pointer - // - Thread B derefs weak pointer - // - Thread B deletes object - BackgroundThread thread; - thread.Start(); - Producer* producer = new Producer(); +TEST(WeakPtrTest, MoveOwnershipImplicitly) { + // Move object ownership to another thread by releasing all weak pointers + // on the original thread first, and then establish WeakPtr on a different + // thread. + BackgroundThread background; + background.Start(); + + Target* target = new Target(); + { + WeakPtr<Target> weak_ptr = target->AsWeakPtr(); + // Main thread deletes the WeakPtr, then the thread ownership of the + // object can be implicitly moved. + } + Arrow* arrow; + + // Background thread creates WeakPtr(and implicitly owns the object). + background.CreateArrowFromTarget(&arrow, target); + EXPECT_EQ(background.DeRef(arrow), target); + { - WeakPtr<Producer> weak_ptr = producer->AsWeakPtr(); + // Main thread creates another WeakPtr, but this does not trigger implicitly + // thread ownership move. + Arrow arrow; + arrow.target = target->AsWeakPtr(); + + // The new WeakPtr is owned by background thread. + EXPECT_EQ(target, background.DeRef(&arrow)); } - Consumer* consumer; - thread.CreateConsumerFromProducer(&consumer, producer); - EXPECT_EQ(thread.DeRef(consumer), producer); - thread.DeleteProducer(producer); - thread.DeleteConsumer(consumer); + + // Target can only be deleted on background thread. + background.DeleteTarget(target); + background.DeleteArrow(arrow); } -TEST(WeakPtrTest, MoveOwnershipExplicit) { - // Test that we do not trip any checks if we establish weak references - // on one thread and delete the object on another thread after explicit - // detachment. - // - Thread A creates object - // - Thread B creates weak pointer - // - Thread B releases weak pointer - // - Detach owner from Thread B - // - Thread A destroys object - BackgroundThread thread; - thread.Start(); - Producer producer; - Consumer* consumer; - thread.CreateConsumerFromProducer(&consumer, &producer); - EXPECT_EQ(thread.DeRef(consumer), &producer); - thread.DeleteConsumer(consumer); - producer.DetachFromThread(); +TEST(WeakPtrTest, MoveOwnershipExplicitlyObjectNotReferenced) { + // Case 1: The target is not bound to any thread yet. So calling + // DetachFromThread() is a no-op. + Target target; + target.DetachFromThread(); + + // Case 2: The target is bound to main thread but no WeakPtr is pointing to + // it. In this case, it will be re-bound to any thread trying to get a + // WeakPtr pointing to it. So detach function call is again no-op. + { + WeakPtr<Target> weak_ptr = target.AsWeakPtr(); + } + target.DetachFromThread(); } -TEST(WeakPtrTest, ThreadARefOutlivesThreadBRef) { +TEST(WeakPtrTest, MoveOwnershipExplicitly) { + BackgroundThread background; + background.Start(); + + Arrow* arrow; + { + Target target; + // Background thread creates WeakPtr(and implicitly owns the object). + background.CreateArrowFromTarget(&arrow, &target); + EXPECT_EQ(&target, background.DeRef(arrow)); + + // Detach from background thread. + target.DetachFromThread(); + + // Re-bind to main thread. + EXPECT_EQ(&target, arrow->target.get()); + + // Main thread can now delete the target. + } + + // WeakPtr can be deleted on non-owner thread. + background.DeleteArrow(arrow); +} + +TEST(WeakPtrTest, MainThreadRefOutlivesBackgroundThreadRef) { // Originating thread has a WeakPtr that outlives others. - // - Thread A creates WeakPtr<> and passes copy to Thread B - // - Destruct the pointer on Thread B - // - Destruct the pointer on Thread A - BackgroundThread thread; - thread.Start(); - Producer producer; - Consumer consumer; - consumer.producer = producer.AsWeakPtr(); - Consumer* consumer_copy; - thread.CreateConsumerFromConsumer(&consumer_copy, &consumer); - EXPECT_EQ(consumer_copy->producer, &producer); - thread.DeleteConsumer(consumer_copy); + // - Main thread creates a WeakPtr + // - Background thread creates a WeakPtr copy from the one in main thread + // - Destruct the WeakPtr on background thread + // - Destruct the WeakPtr on main thread + BackgroundThread background; + background.Start(); + + Target target; + Arrow arrow; + arrow.target = target.AsWeakPtr(); + + Arrow* arrow_copy; + background.CreateArrowFromArrow(&arrow_copy, &arrow); + EXPECT_EQ(arrow_copy->target, &target); + background.DeleteArrow(arrow_copy); } -TEST(WeakPtrTest, ThreadBRefOutlivesThreadARef) { +TEST(WeakPtrTest, BackgroundThreadRefOutlivesMainThreadRef) { // Originating thread drops all references before another thread. - // - Thread A creates WeakPtr<> and passes copy to Thread B - // - Destruct the pointer on Thread A - // - Destruct the pointer on Thread B - BackgroundThread thread; - thread.Start(); - Producer producer; - Consumer* consumer_copy; + // - Main thread creates a WeakPtr and passes copy to background thread + // - Destruct the pointer on main thread + // - Destruct the pointer on background thread + BackgroundThread background; + background.Start(); + + Target target; + Arrow* arrow_copy; { - Consumer consumer; - consumer.producer = producer.AsWeakPtr(); - thread.CreateConsumerFromConsumer(&consumer_copy, &consumer); + Arrow arrow; + arrow.target = target.AsWeakPtr(); + background.CreateArrowFromArrow(&arrow_copy, &arrow); } - EXPECT_EQ(consumer_copy->producer, &producer); - thread.DeleteConsumer(consumer_copy); + EXPECT_EQ(arrow_copy->target, &target); + background.DeleteArrow(arrow_copy); } TEST(WeakPtrTest, OwnerThreadDeletesObject) { // Originating thread invalidates WeakPtrs while its held by other thread. - // - Thread A creates WeakPtr<> and passes Copy to Thread B - // - WeakReferenceOwner gets destroyed on Thread A + // - Main thread creates WeakPtr and passes Copy to background thread + // - Object gets destroyed on main thread + // (invalidates WeakPtr on background thread) // - WeakPtr gets destroyed on Thread B - BackgroundThread thread; - thread.Start(); - Consumer* consumer_copy; + BackgroundThread background; + background.Start(); + Arrow* arrow_copy; { - Producer producer; - Consumer consumer; - consumer.producer = producer.AsWeakPtr(); - thread.CreateConsumerFromConsumer(&consumer_copy, &consumer); + Target target; + Arrow arrow; + arrow.target = target.AsWeakPtr(); + background.CreateArrowFromArrow(&arrow_copy, &arrow); } - EXPECT_TRUE(consumer_copy->producer == NULL); - thread.DeleteConsumer(consumer_copy); + EXPECT_EQ(NULL, arrow_copy->target.get()); + background.DeleteArrow(arrow_copy); } -TEST(WeakPtrTest, Dereference) { - Base data; - data.member = "123456"; - WeakPtrFactory<Base> factory(&data); - WeakPtr<Base> ptr = factory.GetWeakPtr(); - EXPECT_EQ(&data, ptr.get()); - EXPECT_EQ(data.member, (*ptr).member); - EXPECT_EQ(data.member, ptr->member); +TEST(WeakPtrTest, NonOwnerThreadCanDeleteWeakPtr) { + // Main thread creates a Target object. + Target target; + // Main thread creates an arrow referencing the Target. + Arrow* arrow = new Arrow(); + arrow->target = target.AsWeakPtr(); + + // Background can delete arrow (as well as the WeakPtr inside). + BackgroundThread background; + background.Start(); + background.DeleteArrow(arrow); } +#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST + +TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) { + // The default style "fast" does not support multi-threaded tests + // (introduces deadlock on Linux). + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + BackgroundThread background; + background.Start(); + + // Main thread creates a Target object. + Target target; + // Main thread creates an arrow referencing the Target. + Arrow arrow; + arrow.target = target.AsWeakPtr(); + + // Background copies the WeakPtr. + Arrow* arrow_copy; + background.CreateArrowFromArrow(&arrow_copy, &arrow); + + // The copy is still bound to main thread so I can deref. + EXPECT_EQ(arrow.target.get(), arrow_copy->target.get()); + + // Although background thread created the copy, it can not deref the copied + // WeakPtr. + ASSERT_DEATH(background.DeRef(arrow_copy), ""); + + background.DeleteArrow(arrow_copy); +} + +TEST(WeakPtrDeathTest, NonOwnerThreadDereferencesWeakPtr) { + // The default style "fast" does not support multi-threaded tests + // (introduces deadlock on Linux). + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + // Main thread creates a Target object. + Target target; + + // Main thread creates an arrow referencing the Target (so target's + // thread ownership can not be implicitly moved). + Arrow arrow; + arrow.target = target.AsWeakPtr(); + + // Background thread tries to deref target, which violates thread ownership. + BackgroundThread background; + background.Start(); + ASSERT_DEATH(background.DeRef(&arrow), ""); +} + +TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObject) { + // The default style "fast" does not support multi-threaded tests + // (introduces deadlock on Linux). + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + scoped_ptr<Target> target(new Target()); + // Main thread creates an arrow referencing the Target (so target's thread + // ownership can not be implicitly moved). + Arrow arrow; + arrow.target = target->AsWeakPtr(); + + // Background thread tries to delete target, which violates thread ownership. + BackgroundThread background; + background.Start(); + ASSERT_DEATH(background.DeleteTarget(target.release()), ""); +} + +#endif + } // namespace base |