Avoid ref-churn when move conversion constructing RetainPtrs
Prior to this, move constructing from a different, but implicitly
convertible RetainPtr type would invoke a copy, as shown by the
new test.
-- add test for copy conversion constructor.
Change-Id: I9cd0e88eb143efa793d09d5e50ec559df92bb21f
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/97250
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/retain_ptr.h b/core/fxcrt/retain_ptr.h
index 87e22f7..4087dbb 100644
--- a/core/fxcrt/retain_ptr.h
+++ b/core/fxcrt/retain_ptr.h
@@ -32,18 +32,29 @@
}
RetainPtr() = default;
+
+ // Copy-construct a RetainPtr.
+ // Required in addition to copy conversion constructor below.
RetainPtr(const RetainPtr& that) : RetainPtr(that.Get()) {}
// Move-construct a RetainPtr. After construction, |that| will be NULL.
- RetainPtr(RetainPtr&& that) noexcept { Swap(that); }
+ // Required in addition to move conversion constructor below.
+ RetainPtr(RetainPtr&& that) noexcept { Unleak(that.Leak()); }
// Deliberately implicit to allow returning nullptrs.
// NOLINTNEXTLINE(runtime/explicit)
RetainPtr(std::nullptr_t ptr) {}
+ // Copy conversion constructor.
template <class U>
RetainPtr(const RetainPtr<U>& that) : RetainPtr(that.Get()) {}
+ // Move-conversion constructor.
+ template <class U>
+ RetainPtr(RetainPtr<U>&& that) noexcept {
+ Unleak(that.Leak());
+ }
+
template <class U>
RetainPtr<U> As() const {
return RetainPtr<U>(static_cast<U*>(Get()));
@@ -127,6 +138,9 @@
Retainable(const Retainable& that) = delete;
Retainable& operator=(const Retainable& that) = delete;
+ // These need to be const methods operating on a mutable member so that
+ // RetainPtr<const T> can be used for an object that is otherwise const
+ // apart from the internal ref-counting.
void Retain() const { ++m_nRefCount; }
void Release() const {
DCHECK(m_nRefCount > 0);
diff --git a/core/fxcrt/retain_ptr_unittest.cpp b/core/fxcrt/retain_ptr_unittest.cpp
index 20c597d..655a964 100644
--- a/core/fxcrt/retain_ptr_unittest.cpp
+++ b/core/fxcrt/retain_ptr_unittest.cpp
@@ -45,6 +45,22 @@
EXPECT_EQ(2, obj.release_count());
}
+TEST(RetainPtr, CopyConversionCtor) {
+ PseudoRetainable obj;
+ {
+ RetainPtr<PseudoRetainable> ptr1(&obj);
+ {
+ RetainPtr<const PseudoRetainable> ptr2(ptr1);
+ EXPECT_EQ(2, obj.retain_count());
+ EXPECT_EQ(0, obj.release_count());
+ }
+ EXPECT_EQ(2, obj.retain_count());
+ EXPECT_EQ(1, obj.release_count());
+ }
+ EXPECT_EQ(2, obj.retain_count());
+ EXPECT_EQ(2, obj.release_count());
+}
+
TEST(RetainPtr, MoveCtor) {
PseudoRetainable obj;
{
@@ -63,6 +79,24 @@
EXPECT_EQ(1, obj.release_count());
}
+TEST(RetainPtr, MoveConversionCtor) {
+ PseudoRetainable obj;
+ {
+ RetainPtr<PseudoRetainable> ptr1(&obj);
+ {
+ RetainPtr<const PseudoRetainable> ptr2(std::move(ptr1));
+ EXPECT_FALSE(ptr1.Get());
+ EXPECT_EQ(&obj, ptr2.Get());
+ EXPECT_EQ(1, obj.retain_count());
+ EXPECT_EQ(0, obj.release_count());
+ }
+ EXPECT_EQ(1, obj.retain_count());
+ EXPECT_EQ(1, obj.release_count());
+ }
+ EXPECT_EQ(1, obj.retain_count());
+ EXPECT_EQ(1, obj.release_count());
+}
+
TEST(RetainPtr, ResetNull) {
PseudoRetainable obj;
{
diff --git a/testing/pseudo_retainable.h b/testing/pseudo_retainable.h
index c4390d6..45f2b04 100644
--- a/testing/pseudo_retainable.h
+++ b/testing/pseudo_retainable.h
@@ -8,8 +8,8 @@
class PseudoRetainable {
public:
PseudoRetainable() = default;
- void Retain() { ++retain_count_; }
- void Release() {
+ void Retain() const { ++retain_count_; }
+ void Release() const {
if (++release_count_ == retain_count_)
alive_ = false;
}
@@ -18,9 +18,9 @@
int release_count() const { return release_count_; }
private:
- bool alive_ = true;
- int retain_count_ = 0;
- int release_count_ = 0;
+ mutable bool alive_ = true;
+ mutable int retain_count_ = 0;
+ mutable int release_count_ = 0;
};
#endif // TESTING_PSEUDO_RETAINABLE_H_