blob: 8f8a5149e55fed36203ca540897e0d19883defb6 [file] [log] [blame]
// Copyright (c) 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_REFERENCE_COUNTED_IMPL_H_
#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_REFERENCE_COUNTED_IMPL_H_
#include <atomic>
#include <memory>
#include "absl/base/attributes.h"
#include "quiche/common/platform/api/quiche_export.h"
#include "quiche/common/platform/api/quiche_logging.h"
namespace quiche {
class QUICHE_EXPORT_PRIVATE QuicheReferenceCountedImpl {
public:
virtual ~QuicheReferenceCountedImpl() { QUICHE_DCHECK_EQ(ref_count_, 0); }
void AddReference() { ref_count_.fetch_add(1, std::memory_order_relaxed); }
// Returns true if the objects needs to be deleted.
ABSL_MUST_USE_RESULT bool RemoveReference() {
int new_count = ref_count_.fetch_sub(1, std::memory_order_acq_rel) - 1;
QUICHE_DCHECK_GE(new_count, 0);
return new_count == 0;
}
bool HasUniqueReference() const {
return ref_count_.load(std::memory_order_acquire) == 1;
}
private:
std::atomic<int> ref_count_ = 1;
};
template <class T>
class QUICHE_NO_EXPORT QuicheReferenceCountedPointerImpl {
public:
QuicheReferenceCountedPointerImpl() = default;
~QuicheReferenceCountedPointerImpl() { RemoveReference(); }
// Constructor from raw pointer |p|. This guarantees that the reference count
// of *p is 1. This should be only called when a new object is created.
explicit QuicheReferenceCountedPointerImpl(T* p) : object_(p) {
if (p != nullptr) {
QUICHE_DCHECK(p->HasUniqueReference());
}
}
explicit QuicheReferenceCountedPointerImpl(std::nullptr_t)
: object_(nullptr) {}
// Copy and copy conversion constructors.
QuicheReferenceCountedPointerImpl(
const QuicheReferenceCountedPointerImpl& other) {
AssignObject(other.get());
AddReference();
}
template <typename U>
QuicheReferenceCountedPointerImpl( // NOLINT
const QuicheReferenceCountedPointerImpl<U>& other) {
AssignObject(other.get());
AddReference();
}
// Move constructors.
QuicheReferenceCountedPointerImpl(QuicheReferenceCountedPointerImpl&& other) {
object_ = other.object_;
other.object_ = nullptr;
}
template <typename U>
QuicheReferenceCountedPointerImpl(
QuicheReferenceCountedPointerImpl<U>&& other) { // NOLINT
// We can't access other.object_ since other has different T and object_ is
// private.
object_ = other.get();
AddReference();
other = nullptr;
}
// Copy assignments.
QuicheReferenceCountedPointerImpl& operator=(
const QuicheReferenceCountedPointerImpl& other) {
AssignObject(other.object_);
AddReference();
return *this;
}
template <typename U>
QuicheReferenceCountedPointerImpl<T>& operator=(
const QuicheReferenceCountedPointerImpl<U>& other) {
AssignObject(other.object_);
AddReference();
return *this;
}
// Move assignments.
QuicheReferenceCountedPointerImpl& operator=(
QuicheReferenceCountedPointerImpl&& other) {
AssignObject(other.object_);
other.object_ = nullptr;
return *this;
}
template <typename U>
QuicheReferenceCountedPointerImpl<T>& operator=(
QuicheReferenceCountedPointerImpl<U>&& other) {
AssignObject(other.get());
AddReference();
other = nullptr;
return *this;
}
T& operator*() const { return *object_; }
T* operator->() const { return object_; }
explicit operator bool() const { return object_ != nullptr; }
// Assignment operator on raw pointer. Behaves similar to the raw pointer
// constructor.
QuicheReferenceCountedPointerImpl<T>& operator=(T* p) {
AssignObject(p);
if (p != nullptr) {
QUICHE_DCHECK(p->HasUniqueReference());
}
return *this;
}
// Returns the raw pointer with no change in reference count.
T* get() const { return object_; }
// Comparisons against same type.
friend bool operator==(const QuicheReferenceCountedPointerImpl& a,
const QuicheReferenceCountedPointerImpl& b) {
return a.get() == b.get();
}
friend bool operator!=(const QuicheReferenceCountedPointerImpl& a,
const QuicheReferenceCountedPointerImpl& b) {
return a.get() != b.get();
}
// Comparisons against nullptr.
friend bool operator==(const QuicheReferenceCountedPointerImpl& a,
std::nullptr_t) {
return a.get() == nullptr;
}
friend bool operator==(std::nullptr_t,
const QuicheReferenceCountedPointerImpl& b) {
return nullptr == b.get();
}
friend bool operator!=(const QuicheReferenceCountedPointerImpl& a,
std::nullptr_t) {
return a.get() != nullptr;
}
friend bool operator!=(std::nullptr_t,
const QuicheReferenceCountedPointerImpl& b) {
return nullptr != b.get();
}
private:
void AddReference() {
if (object_ == nullptr) {
return;
}
QuicheReferenceCountedImpl* implicitly_cast_object = object_;
implicitly_cast_object->AddReference();
}
void RemoveReference() {
if (object_ == nullptr) {
return;
}
QuicheReferenceCountedImpl* implicitly_cast_object = object_;
if (implicitly_cast_object->RemoveReference()) {
delete implicitly_cast_object;
}
object_ = nullptr;
}
void AssignObject(T* new_object) {
RemoveReference();
object_ = new_object;
}
T* object_ = nullptr;
};
} // namespace quiche
#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_REFERENCE_COUNTED_IMPL_H_