blob: beaa34075f0e5f9a83f803d2a143e7bee52985cc [file] [log] [blame] [edit]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_BIN_REFERENCE_COUNTING_H_
#define RUNTIME_BIN_REFERENCE_COUNTING_H_
#include <atomic>
#include "platform/assert.h"
namespace dart {
namespace bin {
// Forward declaration.
template <class Target>
class RefCntReleaseScope;
// Inherit from this class where instances of the derived class should be
// reference counted. Reference counts on instances are incremented and
// decremented explicitly with calls to Retain() and Release(). E.g.:
//
// class Foo : public ReferenceCounted<Foo> {
// public:
// Foo() : ReferenceCounted() {}
// ...
// };
//
// void DoStuffWithAFoo() {
// Foo* foo = new Foo(); // Reference count starts at 1, so no explicit
// // call to Retain is needed after allocation.
// ...
// foo->Release();
// }
template <class Derived>
class ReferenceCounted {
public:
ReferenceCounted() : ref_count_(1) {
#if defined(DEBUG)
instances_.fetch_add(1u, std::memory_order_relaxed);
#endif // defined(DEBUG)
}
virtual ~ReferenceCounted() {
ASSERT(ref_count_ == 0);
#if defined(DEBUG)
instances_.fetch_sub(1u, std::memory_order_relaxed);
#endif // defined(DEBUG)
}
void Retain() {
intptr_t old = ref_count_.fetch_add(1u, std::memory_order_relaxed);
ASSERT(old > 0);
}
void Release() {
intptr_t old = ref_count_.fetch_sub(1u, std::memory_order_acq_rel);
ASSERT(old > 0);
if (old == 1) {
delete static_cast<Derived*>(this);
}
}
#if defined(DEBUG)
static intptr_t instances() {
return instances_.load(std::memory_order_relaxed);
}
#endif // defined(DEBUG)
private:
#if defined(DEBUG)
static std::atomic<intptr_t> instances_;
#endif // defined(DEBUG)
std::atomic<intptr_t> ref_count_;
// These are used only in the ASSERT below in RefCntReleaseScope.
intptr_t ref_count() const {
return ref_count_.load(std::memory_order_relaxed);
}
friend class RefCntReleaseScope<Derived>;
DISALLOW_COPY_AND_ASSIGN(ReferenceCounted);
};
#if defined(DEBUG)
template <class Derived>
std::atomic<intptr_t> ReferenceCounted<Derived>::instances_ = {0};
#endif
// Creates a scope at the end of which a reference counted object is
// Released. This is useful for reference counted objects received by the IO
// Service, which have already been Retained E.g.:
//
// CObject* Foo::FooRequest(const CObjectArray& request) {
// Foo* foo = CObjectToFoo(request[0]);
// RefCntReleaseScope<Foo> rs(foo);
// ...
// }
template <class Target>
class RefCntReleaseScope {
public:
explicit RefCntReleaseScope(ReferenceCounted<Target>* t) : target_(t) {
ASSERT(target_ != nullptr);
ASSERT(target_->ref_count() > 0);
}
~RefCntReleaseScope() { target_->Release(); }
private:
ReferenceCounted<Target>* target_;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(RefCntReleaseScope);
};
// Instances of RetainedPointer manage Retaining and Releasing reference counted
// objects. There are two ways to use it. First, it can be used as a field in
// a class, e.g.:
//
// class Foo {
// private:
// RetainedPointer<Bar> bar_;
// public:
// explicit Foo(Bar* b) : bar_(b) {}
// }
//
// In this case, b will be Retained in Foo's constructor, and Released
// automatically during Foo's destructor.
//
// RetainedPointer can also be used as a scope, as with RefCntReleaseScope,
// with the difference that entering the scope also Retains the pointer, e.g.:
//
// void RetainAndDoStuffWithFoo(Foo* foo) {
// RetainedPointer<Foo> retained(foo);
// ..
// }
//
// This Retains foo on entry and Releases foo at every exit from the scope.
//
// The underlying pointer can be accessed with the get() and set() methods.
// Overwriting a non-null pointer with set causes that pointer to be Released.
template <class Target>
class RetainedPointer {
public:
RetainedPointer() : target_(nullptr) {}
explicit RetainedPointer(ReferenceCounted<Target>* t) : target_(t) {
if (target_ != nullptr) {
target_->Retain();
}
}
~RetainedPointer() {
if (target_ != nullptr) {
target_->Release();
}
}
void set(ReferenceCounted<Target>* t) {
if (target_ != nullptr) {
target_->Release();
}
target_ = t;
if (target_ != nullptr) {
target_->Retain();
}
}
Target* get() const { return static_cast<Target*>(target_); }
private:
ReferenceCounted<Target>* target_;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(RetainedPointer);
};
} // namespace bin
} // namespace dart
#endif // RUNTIME_BIN_REFERENCE_COUNTING_H_