root/base/memory/weak_ptr_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. NewObject
  2. CreateObject
  3. CreateArrowFromTarget
  4. CreateArrowFromArrow
  5. DeleteTarget
  6. CopyAndAssignArrow
  7. CopyAndAssignArrowBase
  8. DeleteArrow
  9. DeRef
  10. DoCreateArrowFromArrow
  11. DoCreateArrowFromTarget
  12. DoDeRef
  13. DoDeleteTarget
  14. DoCopyAndAssignArrow
  15. DoCopyAndAssignArrowBase
  16. DoDeleteArrow
  17. TEST
  18. TEST
  19. TEST
  20. TEST
  21. TEST
  22. TEST
  23. TEST
  24. TEST
  25. TEST
  26. TEST
  27. TEST
  28. TEST
  29. TEST
  30. TEST
  31. TEST
  32. TEST
  33. TEST
  34. TEST
  35. TEST
  36. TEST
  37. TEST
  38. TEST
  39. TEST
  40. TEST
  41. TEST
  42. TEST
  43. TEST

// 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/debug/leak_annotations.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/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 : public Base {};

struct TargetBase {};
struct Target : public TargetBase, public SupportsWeakPtr<Target> {
  virtual ~Target() {}
};
struct DerivedTarget : public Target {};
struct Arrow {
  WeakPtr<Target> target;
};
struct TargetWithFactory : public Target {
  TargetWithFactory() : factory(this) {}
  WeakPtrFactory<Target> factory;
};

// 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 CopyAndAssignArrow(Arrow* object) {
    WaitableEvent completion(true, false);
    message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BackgroundThread::DoCopyAndAssignArrow,
                   object, &completion));
    completion.Wait();
  }

  void CopyAndAssignArrowBase(Arrow* object) {
    WaitableEvent completion(true, false);
    message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BackgroundThread::DoCopyAndAssignArrowBase,
                   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 DoCopyAndAssignArrow(Arrow* object, WaitableEvent* completion) {
    // Copy constructor.
    Arrow a = *object;
    // Assignment operator.
    *object = a;
    completion->Signal();
  }

  static void DoCopyAndAssignArrowBase(
      Arrow* object,
      WaitableEvent* completion) {
    // Copy constructor.
    WeakPtr<TargetBase> b = object->target;
    // Assignment operator.
    WeakPtr<TargetBase> c;
    c = object->target;
    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.get(), ptr2.get());
}

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, MoveOwnershipOfUnreferencedObject) {
  BackgroundThread background;
  background.Start();

  Arrow* arrow;
  {
    Target target;
    // Background thread creates WeakPtr.
    background.CreateArrowFromTarget(&arrow, &target);

    // Bind to background thread.
    EXPECT_EQ(&target, background.DeRef(arrow));

    // Release the only WeakPtr.
    arrow->target.reset();

    // Now we should be able to create a new reference from this thread.
    arrow->target = target.AsWeakPtr();

    // Re-bind to main thread.
    EXPECT_EQ(&target, arrow->target.get());

    // And the main thread can now delete the target.
  }

  delete arrow;
}

TEST(WeakPtrTest, MoveOwnershipAfterInvalidate) {
  BackgroundThread background;
  background.Start();

  Arrow arrow;
  scoped_ptr<TargetWithFactory> target(new TargetWithFactory);

  // Bind to main thread.
  arrow.target = target->factory.GetWeakPtr();
  EXPECT_EQ(target.get(), arrow.target.get());

  target->factory.InvalidateWeakPtrs();
  EXPECT_EQ(NULL, arrow.target.get());

  arrow.target = target->factory.GetWeakPtr();
  // Re-bind to background thread.
  EXPECT_EQ(target.get(), background.DeRef(&arrow));

  // And the background thread can now delete the target.
  background.DeleteTarget(target.release());
}

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.get(), &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.get(), &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, NonOwnerThreadCanCopyAndAssignWeakPtr) {
  // 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 copy and assign arrow (as well as the WeakPtr inside).
  BackgroundThread background;
  background.Start();
  background.CopyAndAssignArrow(arrow);
  background.DeleteArrow(arrow);
}

TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtrBase) {
  // 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 copy and assign arrow's WeakPtr to a base class WeakPtr.
  BackgroundThread background;
  background.Start();
  background.CopyAndAssignArrowBase(arrow);
  background.DeleteArrow(arrow);
}

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, NonOwnerThreadDereferencesWeakPtrAfterReference) {
  // 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();
  arrow.target.get();

  // Background thread tries to deref target, which violates thread ownership.
  BackgroundThread background;
  background.Start();
  ASSERT_DEATH(background.DeRef(&arrow), "");
}

TEST(WeakPtrDeathTest, NonOwnerThreadDeletesWeakPtrAfterReference) {
  // 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.
  Arrow arrow;
  arrow.target = target->AsWeakPtr();

  // Background thread tries to deref target, binding it to the thread.
  BackgroundThread background;
  background.Start();
  background.DeRef(&arrow);

  // Main thread deletes Target, violating thread binding.
  ASSERT_DEATH(target.reset(), "");

  // |target.reset()| died so |target| still holds the object, so we
  // must pass it to the background thread to teardown.
  background.DeleteTarget(target.release());
}

TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObjectAfterReference) {
  // 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, and references it, so
  // that it becomes bound to the thread.
  Arrow arrow;
  arrow.target = target->AsWeakPtr();
  arrow.target.get();

  // Background thread tries to delete target, volating thread binding.
  BackgroundThread background;
  background.Start();
  ASSERT_DEATH(background.DeleteTarget(target.release()), "");
}

TEST(WeakPtrDeathTest, NonOwnerThreadReferencesObjectAfterDeletion) {
  // 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.
  Arrow arrow;
  arrow.target = target->AsWeakPtr();

  // Background thread tries to delete target, binding the object to the thread.
  BackgroundThread background;
  background.Start();
  background.DeleteTarget(target.release());

  // Main thread attempts to dereference the target, violating thread binding.
  ASSERT_DEATH(arrow.target.get(), "");
}

#endif

}  // namespace base

/* [<][>][^][v][top][bottom][index][help] */