diff options
author | kkimlabs <kkimlabs@chromium.org> | 2015-04-13 13:49:43 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-13 20:50:01 +0000 |
commit | aa3927982b7cb0032db3f06d89d1c0cfd6479a8b (patch) | |
tree | 7a1789a34eb28fc56c0f7653c3e22772f5015065 | |
parent | 4329e54c3541249dc55174ae9d982a1001118d1b (diff) | |
download | chromium_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.h | 20 | ||||
-rw-r--r-- | base/memory/ref_counted_unittest.cc | 357 | ||||
-rw-r--r-- | base/move.h | 5 |
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_ |