arch, base: Move arm AtomicOpFunctor into the generic header
[gem5.git] / src / base / refcnt.hh
1 /*
2 * Copyright (c) 2017-2018 ARM Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Nathan Binkert
41 */
42
43 #ifndef __BASE_REFCNT_HH__
44 #define __BASE_REFCNT_HH__
45
46 #include <type_traits>
47
48 /**
49 * @file base/refcnt.hh
50 *
51 * Classes for managing reference counted objects.
52 */
53
54 /**
55 * Derive from RefCounted if you want to enable reference counting of
56 * this class. If you want to use automatic reference counting, you
57 * should use RefCountingPtr<T> instead of regular pointers.
58 */
59 class RefCounted
60 {
61 private:
62 // The reference count is mutable because one may want to
63 // reference count a const pointer. This really is OK because
64 // const is about logical constness of the object not really about
65 // strictly disallowing an object to change.
66 mutable int count;
67
68 private:
69 // Don't allow a default copy constructor or copy operator on
70 // these objects because the default operation will copy the
71 // reference count as well and we certainly don't want that.
72 RefCounted(const RefCounted &);
73 RefCounted &operator=(const RefCounted &);
74
75 public:
76 /**
77 * We initialize the reference count to zero and the first object
78 * to take ownership of it must increment it to one.
79 *
80 * @attention A memory leak will occur if you never assign a newly
81 * constructed object to a reference counting pointer.
82 */
83 RefCounted() : count(0) {}
84
85 /**
86 * We make the destructor virtual because we're likely to have
87 * virtual functions on reference counted objects.
88 *
89 * @todo Even if this were true, does it matter? Shouldn't the
90 * derived class indicate this? This only matters if we would
91 * ever choose to delete a "RefCounted *" which I doubt we'd ever
92 * do. We don't ever delete a "void *".
93 */
94 virtual ~RefCounted() {}
95
96 /// Increment the reference count
97 void incref() const { ++count; }
98
99 /// Decrement the reference count and destroy the object if all
100 /// references are gone.
101 void decref() const { if (--count <= 0) delete this; }
102 };
103
104 /**
105 * If you want a reference counting pointer to a mutable object,
106 * create it like this:
107 * @code
108 * typedef RefCountingPtr<Foo> FooPtr;
109 * @endcode
110 *
111 * @attention Do not use "const FooPtr"
112 * To create a reference counting pointer to a const object, use this:
113 * @code
114 * typedef RefCountingPtr<const Foo> ConstFooPtr;
115 * @endcode
116 *
117 * These two usages are analogous to iterator and const_iterator in the stl.
118 */
119 template <class T>
120 class RefCountingPtr
121 {
122 public:
123 using PtrType = T*;
124
125 protected:
126 /** Convenience aliases for const/non-const versions of T w/ friendship. */
127 /** @{ */
128 static constexpr auto TisConst = std::is_const<T>::value;
129 using ConstT = typename std::conditional<TisConst,
130 RefCountingPtr<T>,
131 RefCountingPtr<typename std::add_const<T>::type>>::type;
132 friend ConstT;
133 using NonConstT = typename std::conditional<TisConst,
134 RefCountingPtr<typename std::remove_const<T>::type>,
135 RefCountingPtr<T>>::type;
136 friend NonConstT;
137 /** @} */
138 /// The stored pointer.
139 /// Arguably this should be private.
140 T *data;
141
142 /**
143 * Copy a new pointer value and increment the reference count if
144 * it is a valid pointer. Note, this does not delete the
145 * reference any existing object.
146 * @param d Pointer to store.
147 */
148 void
149 copy(T *d)
150 {
151 data = d;
152 if (data)
153 data->incref();
154 }
155
156 /**
157 * Delete the reference to any existing object if it is non NULL.
158 * @attention this doesn't clear the pointer value, so a double
159 * decref could happen if not careful.
160 */
161 void
162 del()
163 {
164 if (data)
165 data->decref();
166 }
167
168 /**
169 * Drop the old reference and change it to something new.
170 */
171 void
172 set(T *d)
173 {
174 // Need to check if we're actually changing because otherwise
175 // we could delete the last reference before adding the new
176 // reference.
177 if (data != d) {
178 del();
179 copy(d);
180 }
181 }
182
183 public:
184 /// Create an empty reference counting pointer.
185 RefCountingPtr() : data(0) {}
186
187 /// Create a new reference counting pointer to some object
188 /// (probably something newly created). Adds a reference.
189 RefCountingPtr(T *data) { copy(data); }
190
191 /// Create a new reference counting pointer by copying another
192 /// one. Adds a reference.
193 RefCountingPtr(const RefCountingPtr &r) { copy(r.data); }
194
195 /** Move-constructor.
196 * Does not add a reference.
197 */
198 RefCountingPtr(RefCountingPtr&& r)
199 {
200 data = r.data;
201 r.data = nullptr;
202 }
203
204 template <bool B = TisConst>
205 RefCountingPtr(const NonConstT &r) { copy(r.data); }
206
207 /// Destroy the pointer and any reference it may hold.
208 ~RefCountingPtr() { del(); }
209
210 // The following pointer access functions are const because they
211 // don't actually change the pointer, though the user could change
212 // what is pointed to. This is analagous to a "Foo * const".
213
214 /// Access a member variable.
215 T *operator->() const { return data; }
216
217 /// Dereference the pointer.
218 T &operator*() const { return *data; }
219
220 /// Directly access the pointer itself without taking a reference.
221 T *get() const { return data; }
222
223 template <bool B = TisConst>
224 operator RefCountingPtr<typename std::enable_if<!B, ConstT>::type>()
225 {
226 return RefCountingPtr<const T>(*this);
227 }
228
229 /// Assign a new value to the pointer
230 const RefCountingPtr &operator=(T *p) { set(p); return *this; }
231
232 /// Copy the pointer from another RefCountingPtr
233 const RefCountingPtr &operator=(const RefCountingPtr &r)
234 { return operator=(r.data); }
235
236 /// Move-assign the pointer from another RefCountingPtr
237 const RefCountingPtr &operator=(RefCountingPtr&& r)
238 {
239 /* This happens regardless of whether the pointer is the same or not,
240 * because of the move semantics, the rvalue needs to be 'destroyed'.
241 */
242 del();
243 data = r.data;
244 r.data = nullptr;
245 return *this;
246 }
247
248 /// Check if the pointer is empty
249 bool operator!() const { return data == 0; }
250
251 /// Check if the pointer is non-empty
252 operator bool() const { return data != 0; }
253 };
254
255 /// Check for equality of two reference counting pointers.
256 template<class T>
257 inline bool operator==(const RefCountingPtr<T> &l, const RefCountingPtr<T> &r)
258 { return l.get() == r.get(); }
259
260 /// Check for equality of of a reference counting pointers and a
261 /// regular pointer
262 template<class T>
263 inline bool operator==(const RefCountingPtr<T> &l, const T *r)
264 { return l.get() == r; }
265
266 /// Check for equality of of a reference counting pointers and a
267 /// regular pointer
268 template<class T>
269 inline bool operator==(const T *l, const RefCountingPtr<T> &r)
270 { return l == r.get(); }
271
272 /// Check for inequality of two reference counting pointers.
273 template<class T>
274 inline bool operator!=(const RefCountingPtr<T> &l, const RefCountingPtr<T> &r)
275 { return l.get() != r.get(); }
276
277 /// Check for inequality of of a reference counting pointers and a
278 /// regular pointer
279 template<class T>
280 inline bool operator!=(const RefCountingPtr<T> &l, const T *r)
281 { return l.get() != r; }
282
283 /// Check for inequality of of a reference counting pointers and a
284 /// regular pointer
285 template<class T>
286 inline bool operator!=(const T *l, const RefCountingPtr<T> &r)
287 { return l != r.get(); }
288
289 #endif // __BASE_REFCNT_HH__