From 8df643b38785f9b6fe8ae4bbf295839d28136d08 Mon Sep 17 00:00:00 2001 From: Danil Chapovalov Date: Fri, 22 Jan 2021 16:11:10 +0100 Subject: [PATCH] Introduce FinalRefCountedObject template class To add ref counting to any class while avoiding virtual functions for reference counting. This template can both slightly reduce binary size and slightly improve performance. Bug: webrtc:11308 Change-Id: I90ac735f6c220ee2a1a991a71039acdb0ca86453 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/198845 Commit-Queue: Danil Chapovalov Reviewed-by: Mirko Bonadei Reviewed-by: Niels Moller Cr-Commit-Position: refs/heads/master@{#33058} --- rtc_base/copy_on_write_buffer.cc | 17 +++++++------- rtc_base/copy_on_write_buffer.h | 11 +++++---- rtc_base/ref_counted_object.h | 31 +++++++++++++++++++++++++ rtc_base/ref_counted_object_unittest.cc | 16 +++++++++++++ 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/rtc_base/copy_on_write_buffer.cc b/rtc_base/copy_on_write_buffer.cc index 73182a12b1..f3cc710f85 100644 --- a/rtc_base/copy_on_write_buffer.cc +++ b/rtc_base/copy_on_write_buffer.cc @@ -32,16 +32,15 @@ CopyOnWriteBuffer::CopyOnWriteBuffer(const std::string& s) : CopyOnWriteBuffer(s.data(), s.length()) {} CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size) - : buffer_(size > 0 ? new RefCountedObject(size) : nullptr), + : buffer_(size > 0 ? new RefCountedBuffer(size) : nullptr), offset_(0), size_(size) { RTC_DCHECK(IsConsistent()); } CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity) - : buffer_(size > 0 || capacity > 0 - ? new RefCountedObject(size, capacity) - : nullptr), + : buffer_(size > 0 || capacity > 0 ? new RefCountedBuffer(size, capacity) + : nullptr), offset_(0), size_(size) { RTC_DCHECK(IsConsistent()); @@ -61,7 +60,7 @@ void CopyOnWriteBuffer::SetSize(size_t size) { RTC_DCHECK(IsConsistent()); if (!buffer_) { if (size > 0) { - buffer_ = new RefCountedObject(size); + buffer_ = new RefCountedBuffer(size); offset_ = 0; size_ = size; } @@ -84,7 +83,7 @@ void CopyOnWriteBuffer::EnsureCapacity(size_t new_capacity) { RTC_DCHECK(IsConsistent()); if (!buffer_) { if (new_capacity > 0) { - buffer_ = new RefCountedObject(0, new_capacity); + buffer_ = new RefCountedBuffer(0, new_capacity); offset_ = 0; size_ = 0; } @@ -105,7 +104,7 @@ void CopyOnWriteBuffer::Clear() { if (buffer_->HasOneRef()) { buffer_->Clear(); } else { - buffer_ = new RefCountedObject(0, capacity()); + buffer_ = new RefCountedBuffer(0, capacity()); } offset_ = 0; size_ = 0; @@ -117,8 +116,8 @@ void CopyOnWriteBuffer::UnshareAndEnsureCapacity(size_t new_capacity) { return; } - buffer_ = new RefCountedObject(buffer_->data() + offset_, size_, - new_capacity); + buffer_ = + new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity); offset_ = 0; RTC_DCHECK(IsConsistent()); } diff --git a/rtc_base/copy_on_write_buffer.h b/rtc_base/copy_on_write_buffer.h index 9ec72ee523..526cbe5c5c 100644 --- a/rtc_base/copy_on_write_buffer.h +++ b/rtc_base/copy_on_write_buffer.h @@ -159,9 +159,9 @@ class RTC_EXPORT CopyOnWriteBuffer { void SetData(const T* data, size_t size) { RTC_DCHECK(IsConsistent()); if (!buffer_) { - buffer_ = size > 0 ? new RefCountedObject(data, size) : nullptr; + buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr; } else if (!buffer_->HasOneRef()) { - buffer_ = new RefCountedObject(data, size, capacity()); + buffer_ = new RefCountedBuffer(data, size, capacity()); } else { buffer_->SetData(data, size); } @@ -196,7 +196,7 @@ class RTC_EXPORT CopyOnWriteBuffer { void AppendData(const T* data, size_t size) { RTC_DCHECK(IsConsistent()); if (!buffer_) { - buffer_ = new RefCountedObject(data, size); + buffer_ = new RefCountedBuffer(data, size); offset_ = 0; size_ = size; RTC_DCHECK(IsConsistent()); @@ -242,7 +242,7 @@ class RTC_EXPORT CopyOnWriteBuffer { // Swaps two buffers. friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) { - std::swap(a.buffer_, b.buffer_); + a.buffer_.swap(b.buffer_); std::swap(a.offset_, b.offset_); std::swap(a.size_, b.size_); } @@ -257,6 +257,7 @@ class RTC_EXPORT CopyOnWriteBuffer { } private: + using RefCountedBuffer = FinalRefCountedObject; // Create a copy of the underlying data if it is referenced from other Buffer // objects or there is not enough capacity. void UnshareAndEnsureCapacity(size_t new_capacity); @@ -272,7 +273,7 @@ class RTC_EXPORT CopyOnWriteBuffer { } // buffer_ is either null, or points to an rtc::Buffer with capacity > 0. - scoped_refptr> buffer_; + scoped_refptr buffer_; // This buffer may represent a slice of a original data. size_t offset_; // Offset of a current slice in the original data in buffer_. // Should be 0 if the buffer_ is empty. diff --git a/rtc_base/ref_counted_object.h b/rtc_base/ref_counted_object.h index ce18379d50..e86a1fba0d 100644 --- a/rtc_base/ref_counted_object.h +++ b/rtc_base/ref_counted_object.h @@ -59,6 +59,37 @@ class RefCountedObject : public T { RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject); }; +template +class FinalRefCountedObject final : public T { + public: + using T::T; + // Until c++17 compilers are allowed not to inherit the default constructor, + // and msvc doesn't. Thus the default constructor is forwarded explicitly. + FinalRefCountedObject() = default; + FinalRefCountedObject(const FinalRefCountedObject&) = delete; + FinalRefCountedObject& operator=(const FinalRefCountedObject&) = delete; + + void AddRef() const { ref_count_.IncRef(); } + void Release() const { + if (ref_count_.DecRef() == RefCountReleaseStatus::kDroppedLastRef) { + delete this; + } + } + bool HasOneRef() const { return ref_count_.HasOneRef(); } + + private: + ~FinalRefCountedObject() = default; + + // gcc v7.1 requires default contructors for members of + // `FinalRefCountedObject` to be able to use inherited constructors. + // TODO(danilchap): Replace with simpler braced initialization when + // bot support for that version of gcc is dropped. + class ZeroBasedRefCounter : public webrtc::webrtc_impl::RefCounter { + public: + ZeroBasedRefCounter() : RefCounter(0) {} + } mutable ref_count_; +}; + } // namespace rtc #endif // RTC_BASE_REF_COUNTED_OBJECT_H_ diff --git a/rtc_base/ref_counted_object_unittest.cc b/rtc_base/ref_counted_object_unittest.cc index eacf731782..05380b7ca9 100644 --- a/rtc_base/ref_counted_object_unittest.cc +++ b/rtc_base/ref_counted_object_unittest.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include "api/scoped_refptr.h" @@ -95,4 +96,19 @@ TEST(RefCountedObject, SupportMixedTypesInCtor) { EXPECT_EQ(c, ref->c_); } +TEST(FinalRefCountedObject, CanWrapIntoScopedRefptr) { + using WrappedTyped = FinalRefCountedObject; + static_assert(!std::is_polymorphic::value, ""); + scoped_refptr ref(new WrappedTyped()); + EXPECT_TRUE(ref.get()); + EXPECT_TRUE(ref->HasOneRef()); + // Test reference counter is updated on some simple operations. + scoped_refptr ref2 = ref; + EXPECT_FALSE(ref->HasOneRef()); + EXPECT_FALSE(ref2->HasOneRef()); + + ref = nullptr; + EXPECT_TRUE(ref2->HasOneRef()); +} + } // namespace rtc