mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:31:58 +00:00
453fce9e77
TEST=ci Change-Id: I319c932a6f4b6e18d7e53b66d69702bf017e4b93 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/313060 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
181 lines
4.6 KiB
C++
181 lines
4.6 KiB
C++
// 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_
|