// 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 #include "platform/assert.h" #include "platform/globals.h" namespace dart { namespace bin { // Forward declaration. template 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 { // 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 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(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 instances_; #endif // defined(DEBUG) std::atomic 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; DISALLOW_COPY_AND_ASSIGN(ReferenceCounted); }; #if defined(DEBUG) template std::atomic ReferenceCounted::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 rs(foo); // ... // } template class RefCntReleaseScope { public: explicit RefCntReleaseScope(ReferenceCounted* t) : target_(t) { ASSERT(target_ != nullptr); ASSERT(target_->ref_count() > 0); } ~RefCntReleaseScope() { target_->Release(); } private: ReferenceCounted* 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_; // 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 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 RetainedPointer { public: RetainedPointer() : target_(nullptr) {} explicit RetainedPointer(ReferenceCounted* t) : target_(t) { if (target_ != nullptr) { target_->Retain(); } } ~RetainedPointer() { if (target_ != nullptr) { target_->Release(); } } void set(ReferenceCounted* t) { if (target_ != nullptr) { target_->Release(); } target_ = t; if (target_ != nullptr) { target_->Retain(); } } Target* get() const { return static_cast(target_); } private: ReferenceCounted* target_; DISALLOW_ALLOCATION(); DISALLOW_COPY_AND_ASSIGN(RetainedPointer); }; } // namespace bin } // namespace dart #endif // RUNTIME_BIN_REFERENCE_COUNTING_H_