blob: f2decc80a9391b6fd417c9143df0f284691a6d68 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <fbl/atomic.h>
#include <fbl/canary.h>
namespace fbl {
namespace internal {
template <bool Enabled>
class AdoptionValidator;
// This will catch:
// - Double-adoptions
// - AddRef/Release without adopting first
// - Wrapping bad pointers
// - Re-wrapping raw pointers to destroyed objects
template <>
class AdoptionValidator<true> {
public:
~AdoptionValidator() {
magic_ = 0;
}
void Adopt() const {
AssertMagic(kStartingMagic);
magic_ = kAdoptedMagic;
}
void ValidateAddRef() const {
AssertMagic(kAdoptedMagic);
}
void ValidateRelease() const {
AssertMagic(kAdoptedMagic);
}
private:
void AssertMagic(const uint32_t expected) const {
ZX_DEBUG_ASSERT_MSG(magic_ == expected,
"Invalid magic (expect: 0x%02x, got: 0x%02x)\n",
static_cast<unsigned int>(expected),
static_cast<unsigned int>(magic_));
}
// The object has been constructed, but not yet adopted or destroyed.
static constexpr uint32_t kStartingMagic = fbl::magic("RcST");
// The object has been constructed and adopted, but not destroyed.
static constexpr uint32_t kAdoptedMagic = fbl::magic("RcAD");
mutable volatile uint32_t magic_ = kStartingMagic;
};
template <>
class AdoptionValidator<false> {
public:
void Adopt() const {}
void ValidateAddRef() const {}
void ValidateRelease() const {}
};
template <bool EnableAdoptionValidator>
class RefCountedBase {
protected:
constexpr RefCountedBase()
: ref_count_(1) {}
~RefCountedBase() {}
void AddRef() const {
adoption_validator_.ValidateAddRef();
const int rc = ref_count_.fetch_add(1, memory_order_relaxed);
if (EnableAdoptionValidator) {
// This assertion will fire if someone calls AddRef() on a
// ref-counted object that has reached ref_count_ == 0 but has not
// been destroyed yet. This could happen by manually calling
// AddRef(), or re-wrapping such a pointer with WrapRefPtr() or
// RefPtr<T>(T*) (both of which call AddRef()). If the object has
// already been destroyed, the magic check in
// adoption_validator_.ValidateAddRef() should have caught it before
// this point.
ZX_DEBUG_ASSERT_MSG(rc >= 1, "count %d < 1\n", rc);
}
}
// This method should not be used. See MakeRefPtrUpgradeFromRaw()
// for details in the proper use of this method. The actual job of
// this function is to atomically increment the refcount if the
// refcount is greater than zero.
//
// This method returns false if the object was found with refcount
// of zero and refcount was unmodified and true if the refcount
// was not zero and it was incremented.
//
// The procedure used is the while-CAS loop with the advantage that
// compare_exchange on failure updates |old| on failure (to exchange)
// so the loop does not have to do a separate load.
//
bool AddRefMaybeInDestructor() {
int old = ref_count_.load(memory_order_acquire);
do {
if (old == 0)
return false;
} while (!ref_count_.compare_exchange_weak(
&old, old + 1, memory_order_acquire, memory_order_acquire));
return true;
}
// Returns true if the object should self-delete.
bool Release() const __WARN_UNUSED_RESULT {
adoption_validator_.ValidateRelease();
const int rc = ref_count_.fetch_sub(1, memory_order_release);
if (EnableAdoptionValidator) {
// This assertion will fire if someone manually calls Release()
// on a ref-counted object too many times.
ZX_DEBUG_ASSERT_MSG(rc >= 1, "count %d < 1\n", rc);
}
if (rc == 1) {
atomic_thread_fence(memory_order_acquire);
return true;
}
return false;
}
void Adopt() const {
adoption_validator_.Adopt();
}
// Current ref count. Only to be used for debugging purposes.
int ref_count_debug() const {
return ref_count_.load(memory_order_relaxed);
}
private:
mutable fbl::atomic_int ref_count_;
AdoptionValidator<EnableAdoptionValidator> adoption_validator_;
};
} // namespace internal
} // namespace fbl