blob: d5f8057cc5b88069b35d640dcc130fe81eaa41e8 [file] [log] [blame]
// Copyright (c) 2012 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/memory/weak_ptr.h"
#include <string>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.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,
base::Bind(OffThreadObjectCreator::CreateObject, &result));
}
DCHECK(result); // We synchronized on thread destruction above.
return result;
}
private:
static void CreateObject(T** result) {
*result = new T;
}
};
struct Base {
std::string member;
};
struct Derived : Base {};
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.
class BackgroundThread : public Thread {
public:
BackgroundThread() : Thread("owner_thread") {}
virtual ~BackgroundThread() {
Stop();
}
void CreateArrowFromTarget(Arrow** arrow, Target* target) {
WaitableEvent completion(true, false);
message_loop()->PostTask(
FROM_HERE,
base::Bind(&BackgroundThread::DoCreateArrowFromTarget,
arrow, target, &completion));
completion.Wait();
}
void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) {
WaitableEvent completion(true, false);
message_loop()->PostTask(
FROM_HERE,
base::Bind(&BackgroundThread::DoCreateArrowFromArrow,
arrow, other, &completion));
completion.Wait();
}
void DeleteTarget(Target* object) {
WaitableEvent completion(true, false);
message_loop()->PostTask(
FROM_HERE,
base::Bind(&BackgroundThread::DoDeleteTarget, object, &completion));
completion.Wait();
}
void DeleteArrow(Arrow* object) {
WaitableEvent completion(true, false);
message_loop()->PostTask(
FROM_HERE,
base::Bind(&BackgroundThread::DoDeleteArrow, object, &completion));
completion.Wait();
}
Target* DeRef(const Arrow* arrow) {
WaitableEvent completion(true, false);
Target* result = NULL;
message_loop()->PostTask(
FROM_HERE,
base::Bind(&BackgroundThread::DoDeRef, arrow, &result, &completion));
completion.Wait();
return result;
}
protected:
static void DoCreateArrowFromArrow(Arrow** arrow,
const Arrow* other,
WaitableEvent* completion) {
*arrow = new Arrow;
**arrow = *other;
completion->Signal();
}
static void DoCreateArrowFromTarget(Arrow** arrow,
Target* target,
WaitableEvent* completion) {
*arrow = new Arrow;
(*arrow)->target = target->AsWeakPtr();
completion->Signal();
}
static void DoDeRef(const Arrow* arrow,
Target** result,
WaitableEvent* completion) {
*result = arrow->target.get();
completion->Signal();
}
static void DoDeleteTarget(Target* object, WaitableEvent* completion) {
delete object;
completion->Signal();
}
static void DoDeleteArrow(Arrow* object, WaitableEvent* completion) {
delete object;
completion->Signal();
}
};
} // namespace
TEST(WeakPtrFactoryTest, Basic) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_EQ(&data, ptr.get());
}
TEST(WeakPtrFactoryTest, Comparison) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
WeakPtr<int> ptr2 = ptr;
EXPECT_EQ(ptr, ptr2);
}
TEST(WeakPtrFactoryTest, OutOfScope) {
WeakPtr<int> ptr;
EXPECT_EQ(NULL, ptr.get());
{
int data;
WeakPtrFactory<int> factory(&data);
ptr = factory.GetWeakPtr();
}
EXPECT_EQ(NULL, ptr.get());
}
TEST(WeakPtrFactoryTest, 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(WeakPtrFactoryTest, MultipleStaged) {
WeakPtr<int> a;
{
int data;
WeakPtrFactory<int> factory(&data);
a = factory.GetWeakPtr();
{
WeakPtr<int> b = factory.GetWeakPtr();
}
EXPECT_TRUE(NULL != a.get());
}
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(WeakPtrFactoryTest, UpCast) {
Derived data;
WeakPtrFactory<Derived> factory(&data);
WeakPtr<Base> ptr = factory.GetWeakPtr();
ptr = factory.GetWeakPtr();
EXPECT_EQ(ptr.get(), &data);
}
TEST(WeakPtrTest, SupportsWeakPtr) {
Target target;
WeakPtr<Target> ptr = target.AsWeakPtr();
EXPECT_EQ(&target, ptr.get());
}
TEST(WeakPtrTest, DerivedTarget) {
DerivedTarget target;
WeakPtr<DerivedTarget> ptr = AsWeakPtr(&target);
EXPECT_EQ(&target, ptr.get());
}
TEST(WeakPtrTest, InvalidateWeakPtrs) {
int data;
WeakPtrFactory<int> factory(&data);
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_EQ(&data, ptr.get());
EXPECT_TRUE(factory.HasWeakPtrs());
factory.InvalidateWeakPtrs();
EXPECT_EQ(NULL, ptr.get());
EXPECT_FALSE(factory.HasWeakPtrs());
}
TEST(WeakPtrTest, HasWeakPtrs) {
int data;
WeakPtrFactory<int> factory(&data);
{
WeakPtr<int> ptr = factory.GetWeakPtr();
EXPECT_TRUE(factory.HasWeakPtrs());
}
EXPECT_FALSE(factory.HasWeakPtrs());
}
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, 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 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, 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);
{
// 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));
}
// Target can only be deleted on background thread.
background.DeleteTarget(target);
background.DeleteArrow(arrow);
}
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, 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.
// - 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, BackgroundThreadRefOutlivesMainThreadRef) {
// Originating thread drops all references before another thread.
// - 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;
{
Arrow arrow;
arrow.target = target.AsWeakPtr();
background.CreateArrowFromArrow(&arrow_copy, &arrow);
}
EXPECT_EQ(arrow_copy->target, &target);
background.DeleteArrow(arrow_copy);
}
TEST(WeakPtrTest, OwnerThreadDeletesObject) {
// Originating thread invalidates WeakPtrs while its held by other thread.
// - 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 background;
background.Start();
Arrow* arrow_copy;
{
Target target;
Arrow arrow;
arrow.target = target.AsWeakPtr();
background.CreateArrowFromArrow(&arrow_copy, &arrow);
}
EXPECT_EQ(NULL, arrow_copy->target.get());
background.DeleteArrow(arrow_copy);
}
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