#ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
#define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
#include <grpc/support/port_platform.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <atomic>
#include <cassert>
#include <cinttypes>
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/abstract.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
namespace grpc_core {
class PolymorphicRefCount {
public:
GRPC_ABSTRACT_BASE_CLASS
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
virtual ~PolymorphicRefCount() = default;
};
class NonPolymorphicRefCount {
public:
GRPC_ABSTRACT_BASE_CLASS
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
~NonPolymorphicRefCount() = default;
};
class RefCount {
public:
using Value = intptr_t;
constexpr explicit RefCount(Value init = 1) : value_(init) {}
void Ref(Value n = 1) { value_.fetch_add(n, std::memory_order_relaxed); }
void RefNonZero() {
#ifndef NDEBUG
const Value prior = value_.fetch_add(1, std::memory_order_relaxed);
assert(prior > 0);
#else
Ref();
#endif
}
bool Unref() {
const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel);
GPR_DEBUG_ASSERT(prior > 0);
return prior == 1;
}
Value get() const { return value_.load(std::memory_order_relaxed); }
private:
std::atomic<Value> value_;
};
template <typename Child, typename Impl = PolymorphicRefCount>
class RefCounted : public Impl {
public:
RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
IncrementRefCount();
return RefCountedPtr<Child>(static_cast<Child*>(this));
}
void Unref() {
if (refs_.Unref()) {
Delete(static_cast<Child*>(this));
}
}
RefCounted(const RefCounted&) = delete;
RefCounted& operator=(const RefCounted&) = delete;
GRPC_ABSTRACT_BASE_CLASS
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
RefCounted() = default;
~RefCounted() = default;
private:
template <typename T>
friend class RefCountedPtr;
void IncrementRefCount() { refs_.Ref(); }
RefCount refs_;
};
template <typename Child, typename Impl = PolymorphicRefCount>
class RefCountedWithTracing : public Impl {
public:
RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
IncrementRefCount();
return RefCountedPtr<Child>(static_cast<Child*>(this));
}
RefCountedPtr<Child> Ref(const DebugLocation& location,
const char* reason) GRPC_MUST_USE_RESULT {
if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
const RefCount::Value old_refs = refs_.get();
gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
trace_flag_->name(), this, location.file(), location.line(),
old_refs, old_refs + 1, reason);
}
return Ref();
}
void Unref() {
if (refs_.Unref()) {
Delete(static_cast<Child*>(this));
}
}
void Unref(const DebugLocation& location, const char* reason) {
if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
const RefCount::Value old_refs = refs_.get();
gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
trace_flag_->name(), this, location.file(), location.line(),
old_refs, old_refs - 1, reason);
}
Unref();
}
RefCountedWithTracing(const RefCountedWithTracing&) = delete;
RefCountedWithTracing& operator=(const RefCountedWithTracing&) = delete;
GRPC_ABSTRACT_BASE_CLASS
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
RefCountedWithTracing()
: RefCountedWithTracing(static_cast<TraceFlag*>(nullptr)) {}
explicit RefCountedWithTracing(TraceFlag* trace_flag)
: trace_flag_(trace_flag) {}
#ifdef NDEBUG
explicit RefCountedWithTracing(DebugOnlyTraceFlag* trace_flag)
: RefCountedWithTracing() {}
#endif
~RefCountedWithTracing() = default;
private:
template <typename T>
friend class RefCountedPtr;
void IncrementRefCount() { refs_.Ref(); }
TraceFlag* trace_flag_ = nullptr;
RefCount refs_;
};
}
#endif