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:
parent
cbacec52bc
commit
8df643b387
@ -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());
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user