refcnt.hh revision 13474
15222Sksewell@umich.edu/* 25254Sksewell@umich.edu * Copyright (c) 2017 ARM Limited 35254Sksewell@umich.edu * All rights reserved. 45254Sksewell@umich.edu * 55222Sksewell@umich.edu * The license below extends only to copyright in the software and shall 65254Sksewell@umich.edu * not be construed as granting a license to any other intellectual 75254Sksewell@umich.edu * property including but not limited to intellectual property relating 85254Sksewell@umich.edu * to a hardware implementation of the functionality of the software 95254Sksewell@umich.edu * licensed hereunder. You may use the software subject to the license 105254Sksewell@umich.edu * terms below provided that you ensure that this notice is replicated 115254Sksewell@umich.edu * unmodified and in its entirety in all distributions of the software, 125254Sksewell@umich.edu * modified or unmodified, in source code or in binary form. 135254Sksewell@umich.edu * 145254Sksewell@umich.edu * Copyright (c) 2002-2005 The Regents of The University of Michigan 155254Sksewell@umich.edu * All rights reserved. 165222Sksewell@umich.edu * 175254Sksewell@umich.edu * Redistribution and use in source and binary forms, with or without 185254Sksewell@umich.edu * modification, are permitted provided that the following conditions are 195254Sksewell@umich.edu * met: redistributions of source code must retain the above copyright 205254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer; 215254Sksewell@umich.edu * redistributions in binary form must reproduce the above copyright 225254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer in the 235254Sksewell@umich.edu * documentation and/or other materials provided with the distribution; 245254Sksewell@umich.edu * neither the name of the copyright holders nor the names of its 255254Sksewell@umich.edu * contributors may be used to endorse or promote products derived from 265254Sksewell@umich.edu * this software without specific prior written permission. 275254Sksewell@umich.edu * 285222Sksewell@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 295254Sksewell@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 305254Sksewell@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 315254Sksewell@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 325222Sksewell@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 335222Sksewell@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 346379Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 356379Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 365222Sksewell@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 376379Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 385222Sksewell@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 395222Sksewell@umich.edu * 405222Sksewell@umich.edu * Authors: Nathan Binkert 415222Sksewell@umich.edu */ 426378Sgblack@eecs.umich.edu 436378Sgblack@eecs.umich.edu#ifndef __BASE_REFCNT_HH__ 446378Sgblack@eecs.umich.edu#define __BASE_REFCNT_HH__ 456383Sgblack@eecs.umich.edu 466379Sgblack@eecs.umich.edu#include <type_traits> 475222Sksewell@umich.edu 485222Sksewell@umich.edu/** 496378Sgblack@eecs.umich.edu * @file base/refcnt.hh 506379Sgblack@eecs.umich.edu * 516383Sgblack@eecs.umich.edu * Classes for managing reference counted objects. 526379Sgblack@eecs.umich.edu */ 536383Sgblack@eecs.umich.edu 545222Sksewell@umich.edu/** 555222Sksewell@umich.edu * Derive from RefCounted if you want to enable reference counting of 566378Sgblack@eecs.umich.edu * this class. If you want to use automatic reference counting, you 576378Sgblack@eecs.umich.edu * should use RefCountingPtr<T> instead of regular pointers. 585222Sksewell@umich.edu */ 595222Sksewell@umich.educlass RefCounted 605222Sksewell@umich.edu{ 615222Sksewell@umich.edu private: 625222Sksewell@umich.edu // The reference count is mutable because one may want to 636378Sgblack@eecs.umich.edu // reference count a const pointer. This really is OK because 645222Sksewell@umich.edu // const is about logical constness of the object not really about 656378Sgblack@eecs.umich.edu // strictly disallowing an object to change. 665222Sksewell@umich.edu mutable int count; 675222Sksewell@umich.edu 686378Sgblack@eecs.umich.edu private: 696378Sgblack@eecs.umich.edu // Don't allow a default copy constructor or copy operator on 705222Sksewell@umich.edu // these objects because the default operation will copy the 716378Sgblack@eecs.umich.edu // reference count as well and we certainly don't want that. 725222Sksewell@umich.edu RefCounted(const RefCounted &); 735222Sksewell@umich.edu RefCounted &operator=(const RefCounted &); 746378Sgblack@eecs.umich.edu 756378Sgblack@eecs.umich.edu public: 765222Sksewell@umich.edu /** 775222Sksewell@umich.edu * We initialize the reference count to zero and the first object 785222Sksewell@umich.edu * to take ownership of it must increment it to one. 795222Sksewell@umich.edu * 805222Sksewell@umich.edu * @attention A memory leak will occur if you never assign a newly 816378Sgblack@eecs.umich.edu * constructed object to a reference counting pointer. 825222Sksewell@umich.edu */ 836378Sgblack@eecs.umich.edu RefCounted() : count(0) {} 845222Sksewell@umich.edu 855222Sksewell@umich.edu /** 866378Sgblack@eecs.umich.edu * We make the destructor virtual because we're likely to have 876378Sgblack@eecs.umich.edu * virtual functions on reference counted objects. 885222Sksewell@umich.edu * 896378Sgblack@eecs.umich.edu * @todo Even if this were true, does it matter? Shouldn't the 905222Sksewell@umich.edu * derived class indicate this? This only matters if we would 915222Sksewell@umich.edu * ever choose to delete a "RefCounted *" which I doubt we'd ever 926378Sgblack@eecs.umich.edu * do. We don't ever delete a "void *". 936378Sgblack@eecs.umich.edu */ 945222Sksewell@umich.edu virtual ~RefCounted() {} 955222Sksewell@umich.edu 965222Sksewell@umich.edu /// Increment the reference count 976378Sgblack@eecs.umich.edu void incref() const { ++count; } 985222Sksewell@umich.edu 995222Sksewell@umich.edu /// Decrement the reference count and destroy the object if all 1006378Sgblack@eecs.umich.edu /// references are gone. 1016378Sgblack@eecs.umich.edu void decref() const { if (--count <= 0) delete this; } 1025222Sksewell@umich.edu}; 1036378Sgblack@eecs.umich.edu 1045222Sksewell@umich.edu/** 1055222Sksewell@umich.edu * If you want a reference counting pointer to a mutable object, 1065222Sksewell@umich.edu * create it like this: 1075222Sksewell@umich.edu * @code 1086378Sgblack@eecs.umich.edu * typedef RefCountingPtr<Foo> FooPtr; 1096378Sgblack@eecs.umich.edu * @endcode 1105222Sksewell@umich.edu * 1115222Sksewell@umich.edu * @attention Do not use "const FooPtr" 1125222Sksewell@umich.edu * To create a reference counting pointer to a const object, use this: 1135222Sksewell@umich.edu * @code 1146383Sgblack@eecs.umich.edu * typedef RefCountingPtr<const Foo> ConstFooPtr; 1156378Sgblack@eecs.umich.edu * @endcode 1166378Sgblack@eecs.umich.edu * 1176379Sgblack@eecs.umich.edu * These two usages are analogous to iterator and const_iterator in the stl. 1185222Sksewell@umich.edu */ 1195222Sksewell@umich.edutemplate <class T> 1205222Sksewell@umich.educlass RefCountingPtr 1216383Sgblack@eecs.umich.edu{ 1226379Sgblack@eecs.umich.edu public: 1235222Sksewell@umich.edu using PtrType = T*; 1246379Sgblack@eecs.umich.edu 1255222Sksewell@umich.edu protected: 1265222Sksewell@umich.edu /** Convenience aliases for const/non-const versions of T w/ friendship. */ 1275222Sksewell@umich.edu /** @{ */ 1285222Sksewell@umich.edu static constexpr auto TisConst = std::is_const<T>::value; 1295222Sksewell@umich.edu using ConstT = typename std::conditional<TisConst, 1306378Sgblack@eecs.umich.edu RefCountingPtr<T>, 1315222Sksewell@umich.edu RefCountingPtr<typename std::add_const<T>::type>>::type; 1326378Sgblack@eecs.umich.edu friend ConstT; 1336378Sgblack@eecs.umich.edu using NonConstT = typename std::conditional<TisConst, 1345222Sksewell@umich.edu RefCountingPtr<typename std::remove_const<T>::type>, 1356383Sgblack@eecs.umich.edu RefCountingPtr<T>>::type; 1366383Sgblack@eecs.umich.edu friend NonConstT; 1375222Sksewell@umich.edu /** @} */ 1385222Sksewell@umich.edu /// The stored pointer. 1395222Sksewell@umich.edu /// Arguably this should be private. 1405222Sksewell@umich.edu T *data; 1416378Sgblack@eecs.umich.edu 1426378Sgblack@eecs.umich.edu /** 1436378Sgblack@eecs.umich.edu * Copy a new pointer value and increment the reference count if 1445222Sksewell@umich.edu * it is a valid pointer. Note, this does not delete the 1455222Sksewell@umich.edu * reference any existing object. 1465222Sksewell@umich.edu * @param d Pointer to store. 1475222Sksewell@umich.edu */ 1486378Sgblack@eecs.umich.edu void 1496378Sgblack@eecs.umich.edu copy(T *d) 1505222Sksewell@umich.edu { 1515222Sksewell@umich.edu data = d; 1525222Sksewell@umich.edu if (data) 1536378Sgblack@eecs.umich.edu data->incref(); 1546378Sgblack@eecs.umich.edu } 1555222Sksewell@umich.edu 1566383Sgblack@eecs.umich.edu /** 1576379Sgblack@eecs.umich.edu * Delete the reference to any existing object if it is non NULL. 1586379Sgblack@eecs.umich.edu * @attention this doesn't clear the pointer value, so a double 1596379Sgblack@eecs.umich.edu * decref could happen if not careful. 1605222Sksewell@umich.edu */ 1615222Sksewell@umich.edu void 1626378Sgblack@eecs.umich.edu del() 1635222Sksewell@umich.edu { 1645222Sksewell@umich.edu if (data) 1655222Sksewell@umich.edu data->decref(); 1665222Sksewell@umich.edu } 1676379Sgblack@eecs.umich.edu 1686379Sgblack@eecs.umich.edu /** 1696379Sgblack@eecs.umich.edu * Drop the old reference and change it to something new. 1706379Sgblack@eecs.umich.edu */ 1716379Sgblack@eecs.umich.edu void 1726379Sgblack@eecs.umich.edu 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