1// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_BIN_REFERENCE_COUNTING_H_
6#define RUNTIME_BIN_REFERENCE_COUNTING_H_
7
8#include <atomic>
9
10namespace dart {
11namespace bin {
12
13// Forward declaration.
14template <class Target>
15class RefCntReleaseScope;
16
17// Inherit from this class where instances of the derived class should be
18// reference counted. Reference counts on instances are incremented and
19// decremented explicitly with calls to Retain() and Release(). E.g.:
20//
21// class Foo : public ReferenceCounted<Foo> {
22// public:
23// Foo() : ReferenceCounted() {}
24// ...
25// };
26//
27// void DoStuffWithAFoo() {
28// Foo* foo = new Foo(); // Reference count starts at 1, so no explicit
29// // call to Retain is needed after allocation.
30// ...
31// foo->Release();
32// }
33template <class Derived>
34class ReferenceCounted {
35 public:
36 ReferenceCounted() : ref_count_(1) {
37#if defined(DEBUG)
38 instances_.fetch_add(1u, std::memory_order_relaxed);
39#endif // defined(DEBUG)
40 }
41
42 virtual ~ReferenceCounted() {
43 ASSERT(ref_count_ == 0);
44#if defined(DEBUG)
45 instances_.fetch_sub(1u, std::memory_order_relaxed);
46#endif // defined(DEBUG)
47 }
48
49 void Retain() {
50 intptr_t old = ref_count_.fetch_add(1u, std::memory_order_relaxed);
51 ASSERT(old > 0);
52 }
53
54 void Release() {
55 intptr_t old = ref_count_.fetch_sub(1u, std::memory_order_acq_rel);
56 ASSERT(old > 0);
57 if (old == 1) {
58 delete static_cast<Derived*>(this);
59 }
60 }
61
62#if defined(DEBUG)
63 static intptr_t instances() {
64 return instances_.load(std::memory_order_relaxed);
65 }
66#endif // defined(DEBUG)
67
68 private:
69#if defined(DEBUG)
70 static std::atomic<intptr_t> instances_;
71#endif // defined(DEBUG)
72
73 std::atomic<intptr_t> ref_count_;
74
75 // These are used only in the ASSERT below in RefCntReleaseScope.
76 intptr_t ref_count() const {
77 return ref_count_.load(std::memory_order_relaxed);
78 }
79 friend class RefCntReleaseScope<Derived>;
80 DISALLOW_COPY_AND_ASSIGN(ReferenceCounted);
81};
82
83#if defined(DEBUG)
84template <class Derived>
85std::atomic<intptr_t> ReferenceCounted<Derived>::instances_ = {0};
86#endif
87
88// Creates a scope at the end of which a reference counted object is
89// Released. This is useful for reference counted objects received by the IO
90// Service, which have already been Retained E.g.:
91//
92// CObject* Foo::FooRequest(const CObjectArray& request) {
93// Foo* foo = CObjectToFoo(request[0]);
94// RefCntReleaseScope<Foo> rs(foo);
95// ...
96// }
97template <class Target>
98class RefCntReleaseScope {
99 public:
100 explicit RefCntReleaseScope(ReferenceCounted<Target>* t) : target_(t) {
101 ASSERT(target_ != NULL);
102 ASSERT(target_->ref_count() > 0);
103 }
104 ~RefCntReleaseScope() { target_->Release(); }
105
106 private:
107 ReferenceCounted<Target>* target_;
108
109 DISALLOW_ALLOCATION();
110 DISALLOW_COPY_AND_ASSIGN(RefCntReleaseScope);
111};
112
113// Instances of RetainedPointer manage Retaining and Releasing reference counted
114// objects. There are two ways to use it. First, it can be used as a field in
115// a class, e.g.:
116//
117// class Foo {
118// private:
119// RetainedPointer<Bar> bar_;
120// public:
121// explicit Foo(Bar* b) : bar_(b) {}
122// }
123//
124// In this case, b will be Retained in Foo's constructor, and Released
125// automatically during Foo's destructor.
126//
127// RetainedPointer can also be used as a scope, as with RefCntReleaseScope,
128// with the difference that entering the scope also Retains the pointer, e.g.:
129//
130// void RetainAndDoStuffWithFoo(Foo* foo) {
131// RetainedPointer<Foo> retained(foo);
132// ..
133// }
134//
135// This Retains foo on entry and Releases foo at every exit from the scope.
136//
137// The underlying pointer can be accessed with the get() and set() methods.
138// Overwriting a non-NULL pointer with set causes that pointer to be Released.
139template <class Target>
140class RetainedPointer {
141 public:
142 RetainedPointer() : target_(NULL) {}
143
144 explicit RetainedPointer(ReferenceCounted<Target>* t) : target_(t) {
145 if (target_ != NULL) {
146 target_->Retain();
147 }
148 }
149
150 ~RetainedPointer() {
151 if (target_ != NULL) {
152 target_->Release();
153 }
154 }
155
156 void set(ReferenceCounted<Target>* t) {
157 if (target_ != NULL) {
158 target_->Release();
159 }
160 target_ = t;
161 if (target_ != NULL) {
162 target_->Retain();
163 }
164 }
165
166 Target* get() const { return static_cast<Target*>(target_); }
167
168 private:
169 ReferenceCounted<Target>* target_;
170
171 DISALLOW_ALLOCATION();
172 DISALLOW_COPY_AND_ASSIGN(RetainedPointer);
173};
174
175} // namespace bin
176} // namespace dart
177
178#endif // RUNTIME_BIN_REFERENCE_COUNTING_H_
179