summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkkimlabs <kkimlabs@chromium.org>2015-04-13 13:49:43 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-13 20:50:01 +0000
commitaa3927982b7cb0032db3f06d89d1c0cfd6479a8b (patch)
tree7a1789a34eb28fc56c0f7653c3e22772f5015065
parent4329e54c3541249dc55174ae9d982a1001118d1b (diff)
downloadchromium_src-aa3927982b7cb0032db3f06d89d1c0cfd6479a8b.zip
chromium_src-aa3927982b7cb0032db3f06d89d1c0cfd6479a8b.tar.gz
chromium_src-aa3927982b7cb0032db3f06d89d1c0cfd6479a8b.tar.bz2
Add move support for scoped_refptr.
scoped_ptr has - move constructor. - move assignment operator. - .Pass(). This CL adds the same support for scoped_refptr. The main benefit is reducing unnecessary reference counter updates. // Example 1 class Foo { // Without .Pass(), a copy constructor would be called, which // increments and decrements the reference counter unnecessarily. // With .Pass(), a move constructor is called which doesn't update // the reference counter Foo(scoped_refptr<T> t) : t_(t.Pass()) {} const scoped_refptr<T> t_; }; // Example 2 scoped_refptr<Foo> func() { return scoped_refptr<Foo>(new Foo()); } main() { scoped_refptr<Foo> foo; // The following would be done by a copy assignment operator before, // but now a move assignment operator will be used instead. foo = func(); } // Example 3 class Foo : public base::RefCountedThreadSafe<Foo> { ... } void func(scoped_refptr<Foo> foo) { ... } main() { scoped_refptr<Foo> foo(...); // Because it's moved, the reference counter won't be updated. massage_loop->PostTask(FROM_HERE, base::Bind(&func, base::Passed(foo))); } Review URL: https://codereview.chromium.org/1076953002 Cr-Commit-Position: refs/heads/master@{#324910}
-rw-r--r--base/memory/ref_counted.h20
-rw-r--r--base/memory/ref_counted_unittest.cc357
-rw-r--r--base/move.h5
3 files changed, 377 insertions, 5 deletions
diff --git a/base/memory/ref_counted.h b/base/memory/ref_counted.h
index 219437e..5f94b4c 100644
--- a/base/memory/ref_counted.h
+++ b/base/memory/ref_counted.h
@@ -14,6 +14,7 @@
#ifndef NDEBUG
#include "base/logging.h"
#endif
+#include "base/move.h"
#include "base/threading/thread_collision_warner.h"
#include "build/build_config.h"
@@ -264,6 +265,7 @@ class RefCountedData
//
template <class T>
class scoped_refptr {
+ TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_refptr)
public:
typedef T element_type;
@@ -286,6 +288,11 @@ class scoped_refptr {
AddRef(ptr_);
}
+ template <typename U>
+ scoped_refptr(scoped_refptr<U>&& r) : ptr_(r.get()) {
+ r.ptr_ = nullptr;
+ }
+
~scoped_refptr() {
if (ptr_)
Release(ptr_);
@@ -323,6 +330,17 @@ class scoped_refptr {
return *this = r.get();
}
+ scoped_refptr<T>& operator=(scoped_refptr<T>&& r) {
+ scoped_refptr<T>(r.Pass()).swap(*this);
+ return *this;
+ }
+
+ template <typename U>
+ scoped_refptr<T>& operator=(scoped_refptr<U>&& r) {
+ scoped_refptr<T>(r.Pass()).swap(*this);
+ return *this;
+ }
+
void swap(T** pp) {
T* p = ptr_;
ptr_ = *pp;
@@ -334,6 +352,8 @@ class scoped_refptr {
}
private:
+ template <typename U> friend class scoped_refptr;
+
// Allow scoped_refptr<T> to be used in boolean expressions, but not
// implicitly convertible to a real bool (which is dangerous).
//
diff --git a/base/memory/ref_counted_unittest.cc b/base/memory/ref_counted_unittest.cc
index f75cd38..6f8e599 100644
--- a/base/memory/ref_counted_unittest.cc
+++ b/base/memory/ref_counted_unittest.cc
@@ -40,19 +40,71 @@ class ScopedRefPtrToSelf : public base::RefCounted<ScopedRefPtrToSelf> {
static bool was_destroyed() { return was_destroyed_; }
- void SelfDestruct() { self_ptr_ = NULL; }
+ static void reset_was_destroyed() { was_destroyed_ = false; }
+
+ scoped_refptr<ScopedRefPtrToSelf> self_ptr_;
private:
friend class base::RefCounted<ScopedRefPtrToSelf>;
~ScopedRefPtrToSelf() { was_destroyed_ = true; }
static bool was_destroyed_;
-
- scoped_refptr<ScopedRefPtrToSelf> self_ptr_;
};
bool ScopedRefPtrToSelf::was_destroyed_ = false;
+class ScopedRefPtrCountBase : public base::RefCounted<ScopedRefPtrCountBase> {
+ public:
+ ScopedRefPtrCountBase() { ++constructor_count_; }
+
+ static int constructor_count() { return constructor_count_; }
+
+ static int destructor_count() { return destructor_count_; }
+
+ static void reset_count() {
+ constructor_count_ = 0;
+ destructor_count_ = 0;
+ }
+
+ protected:
+ virtual ~ScopedRefPtrCountBase() { ++destructor_count_; }
+
+ private:
+ friend class base::RefCounted<ScopedRefPtrCountBase>;
+
+ static int constructor_count_;
+ static int destructor_count_;
+};
+
+int ScopedRefPtrCountBase::constructor_count_ = 0;
+int ScopedRefPtrCountBase::destructor_count_ = 0;
+
+class ScopedRefPtrCountDerived : public ScopedRefPtrCountBase {
+ public:
+ ScopedRefPtrCountDerived() { ++constructor_count_; }
+
+ static int constructor_count() { return constructor_count_; }
+
+ static int destructor_count() { return destructor_count_; }
+
+ static void reset_count() {
+ constructor_count_ = 0;
+ destructor_count_ = 0;
+ }
+
+ protected:
+ ~ScopedRefPtrCountDerived() override { ++destructor_count_; }
+
+ private:
+ friend class base::RefCounted<ScopedRefPtrCountDerived>;
+
+ static int constructor_count_;
+ static int destructor_count_;
+};
+
+int ScopedRefPtrCountDerived::constructor_count_ = 0;
+int ScopedRefPtrCountDerived::destructor_count_ = 0;
+
} // end namespace
TEST(RefCountedUnitTest, TestSelfAssignment) {
@@ -66,10 +118,24 @@ TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) {
CheckDerivedMemberAccess check;
}
-TEST(RefCountedUnitTest, ScopedRefPtrToSelf) {
+TEST(RefCountedUnitTest, ScopedRefPtrToSelfPointerAssignment) {
+ ScopedRefPtrToSelf::reset_was_destroyed();
+
ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf();
EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed());
- check->SelfDestruct();
+ check->self_ptr_ = nullptr;
+ EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed());
+}
+
+TEST(RefCountedUnitTest, ScopedRefPtrToSelfMoveAssignment) {
+ ScopedRefPtrToSelf::reset_was_destroyed();
+
+ ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf();
+ EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed());
+ // Releasing |check->self_ptr_| will delete |check|.
+ // The move assignment operator must assign |check->self_ptr_| first then
+ // release |check->self_ptr_|.
+ check->self_ptr_ = scoped_refptr<ScopedRefPtrToSelf>();
EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed());
}
@@ -113,3 +179,284 @@ TEST(RefCountedUnitTest, ConvertibleEquality) {
EXPECT_EQ(p1, p2);
EXPECT_EQ(p2, p1);
}
+
+TEST(RefCountedUnitTest, SelfMoveAssignment) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p(raw);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ p = p.Pass();
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(raw, p.get());
+
+ // p goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignment1) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ {
+ scoped_refptr<ScopedRefPtrCountBase> p2;
+
+ p2 = p1.Pass();
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(nullptr, p1.get());
+ EXPECT_EQ(raw, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignment2) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1;
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ {
+ scoped_refptr<ScopedRefPtrCountBase> p2(raw);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ p1 = p2.Pass();
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(raw, p1.get());
+ EXPECT_EQ(nullptr, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentSameInstance1) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ {
+ scoped_refptr<ScopedRefPtrCountBase> p2(p1);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ p1 = p2.Pass();
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(raw, p1.get());
+ EXPECT_EQ(nullptr, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentSameInstance2) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ {
+ scoped_refptr<ScopedRefPtrCountBase> p2(p1);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ p2 = p1.Pass();
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(nullptr, p1.get());
+ EXPECT_EQ(raw, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentDifferentInstances) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1(raw1);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ {
+ ScopedRefPtrCountBase *raw2 = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p2(raw2);
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ p1 = p2.Pass();
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(raw2, p1.get());
+ EXPECT_EQ(nullptr, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentDerived) {
+ ScopedRefPtrCountBase::reset_count();
+ ScopedRefPtrCountDerived::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1(raw1);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+ {
+ ScopedRefPtrCountDerived *raw2 = new ScopedRefPtrCountDerived();
+ scoped_refptr<ScopedRefPtrCountDerived> p2(raw2);
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+ p1 = p2.Pass();
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+ EXPECT_EQ(raw2, p1.get());
+ EXPECT_EQ(nullptr, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveConstructor) {
+ ScopedRefPtrCountBase::reset_count();
+
+ {
+ ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+ scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+ {
+ scoped_refptr<ScopedRefPtrCountBase> p2(p1.Pass());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(nullptr, p1.get());
+ EXPECT_EQ(raw, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveConstructorDerived) {
+ ScopedRefPtrCountBase::reset_count();
+ ScopedRefPtrCountDerived::reset_count();
+
+ {
+ ScopedRefPtrCountDerived *raw1 = new ScopedRefPtrCountDerived();
+ scoped_refptr<ScopedRefPtrCountDerived> p1(raw1);
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+ {
+ scoped_refptr<ScopedRefPtrCountBase> p2(p1.Pass());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+ EXPECT_EQ(nullptr, p1.get());
+ EXPECT_EQ(raw1, p2.get());
+
+ // p2 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count());
+
+ // p1 goes out of scope.
+ }
+ EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+ EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count());
+}
+
diff --git a/base/move.h b/base/move.h
index 06f3f32..91fd0e7 100644
--- a/base/move.h
+++ b/base/move.h
@@ -226,4 +226,9 @@
typedef void MoveOnlyTypeForCPP03; \
private:
+#define TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(type) \
+ public: \
+ type&& Pass() WARN_UNUSED_RESULT { return static_cast<type&&>(*this); } \
+ private:
+
#endif // BASE_MOVE_H_