summaryrefslogtreecommitdiffstats
path: root/base/memory
diff options
context:
space:
mode:
Diffstat (limited to 'base/memory')
-rw-r--r--base/memory/weak_ptr.h33
-rw-r--r--base/memory/weak_ptr_unittest.cc417
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