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 <danilchap@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33058}
This commit is contained in:
Danil Chapovalov 2021-01-22 16:11:10 +01:00 committed by Commit Bot
parent cbacec52bc
commit 8df643b387
4 changed files with 61 additions and 14 deletions

View File

@ -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<Buffer>(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<Buffer>(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<Buffer>(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<Buffer>(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<Buffer>(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>(buffer_->data() + offset_, size_,
new_capacity);
buffer_ =
new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity);
offset_ = 0;
RTC_DCHECK(IsConsistent());
}

View File

@ -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<Buffer>(data, size) : nullptr;
buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr;
} else if (!buffer_->HasOneRef()) {
buffer_ = new RefCountedObject<Buffer>(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<Buffer>(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<Buffer>;
// 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<RefCountedObject<Buffer>> buffer_;
scoped_refptr<RefCountedBuffer> 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.

View File

@ -59,6 +59,37 @@ class RefCountedObject : public T {
RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject);
};
template <class T>
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_

View File

@ -12,6 +12,7 @@
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "api/scoped_refptr.h"
@ -95,4 +96,19 @@ TEST(RefCountedObject, SupportMixedTypesInCtor) {
EXPECT_EQ(c, ref->c_);
}
TEST(FinalRefCountedObject, CanWrapIntoScopedRefptr) {
using WrappedTyped = FinalRefCountedObject<A>;
static_assert(!std::is_polymorphic<WrappedTyped>::value, "");
scoped_refptr<WrappedTyped> ref(new WrappedTyped());
EXPECT_TRUE(ref.get());
EXPECT_TRUE(ref->HasOneRef());
// Test reference counter is updated on some simple operations.
scoped_refptr<WrappedTyped> ref2 = ref;
EXPECT_FALSE(ref->HasOneRef());
EXPECT_FALSE(ref2->HasOneRef());
ref = nullptr;
EXPECT_TRUE(ref2->HasOneRef());
}
} // namespace rtc