refcnt.hh revision 13590
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 */ 59class 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 */ 119template <class T> 120class 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. 256template<class T> 257inline 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 262template<class T> 263inline 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 268template<class T> 269inline bool operator==(const T *l, const RefCountingPtr<T> &r) 270{ return l == r.get(); } 271 272/// Check for inequality of two reference counting pointers. 273template<class T> 274inline 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 279template<class T> 280inline 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 285template<class T> 286inline bool operator!=(const T *l, const RefCountingPtr<T> &r) 287{ return l != r.get(); } 288 289#endif // __BASE_REFCNT_HH__ 290