/home/tetron/hack/vos/libs/vos/vutil/refcount.hh
Go to the documentation of this file.00001 /* 00002 This file is part of the Virtual Object System of 00003 the Interreality project (http://interreality.org). 00004 00005 Copyright (C) 2001-2003 Peter Amstutz 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Lesser General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public 00018 License along with this library; if not, write to the Free Software 00019 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 00021 Peter Amstutz <http://www.interreality.org> 00022 */ 00023 #ifndef _VUTIL_REFCOUNT_HH_ 00024 #define _VUTIL_REFCOUNT_HH_ 00025 00026 /** @file 00027 Defines and inline implements RefCount. 00028 */ 00029 00030 #include <set> 00031 00032 #include <vos/vutil/vutildefs.hh> 00033 #include <boost/thread/mutex.hpp> 00034 #include <boost/thread/recursive_mutex.hpp> 00035 00036 namespace VUtil 00037 { 00038 #ifndef SWIG 00039 class RefCounted; 00040 #endif 00041 00042 /** @class ObjectExciseListener refcount.hh vos/vos/refcount.hh 00043 @ingroup libvutil 00044 00045 Interface by which an application can be notified 00046 that an object wants to be excised. 00047 */ 00048 class VUTIL_API ObjectExciseListener 00049 { 00050 public: 00051 virtual ~ObjectExciseListener(); 00052 00053 /** This will be called by the listened-to object when its 00054 RefCounted::excise() method is called. The application should take any 00055 appropriate measures to remove references (to avoid stale 00056 pointers and/or memory leaks) and then call release(). 00057 */ 00058 virtual void notifyObjectExcise(RefCounted* object) = 0; 00059 }; 00060 00061 /** @class RefCounted refcount.hh vos/vos/refcount.hh 00062 @ingroup libvutil 00063 00064 This is a simple base class for reference counting 00065 objects. It also provides a facility for tracking 00066 references to this object, so that they may be notified 00067 when the application wants this object to be deleted. 00068 You can use vRef to automatically release RefCounted objects 00069 when they would normally be destroyed (go out of scope). 00070 00071 @note If you have a situation where you are doing multiple 00072 inheritance from two or more classes that (directly or indirectly) 00073 themselves inherit from RefCounted, you need to mark your 00074 inheritance as a virtual base class. This means in your code 00075 @code 00076 class Foo : public virtual RefCounted { ... }; 00077 class Bar : public virtual RefCounted { ... }; 00078 class Baz : public virtual Foo, public virtual Bar { ... }; 00079 @endcode 00080 Otherwise you could have two (or more) seperate 00081 reference counters for the same object, and that won't do at all. 00082 */ 00083 #if !defined(SWIG) 00084 class VUTIL_API RefCounted 00085 { 00086 private: 00087 int count; 00088 boost::mutex count_mutex; 00089 00090 std::set<ObjectExciseListener*>* exciseListeners; 00091 boost::recursive_mutex exciseListeners_mutex; 00092 public: 00093 /** Construct the refcount object with a starting count of 1 */ 00094 RefCounted() : count(1), exciseListeners(0) { }; 00095 00096 /** copy constructor, need to reset count on the copy */ 00097 RefCounted(const RefCounted& /*rc*/) : count(1), exciseListeners(0) { }; 00098 00099 /** Destructor. (Cleans up excise listeners) */ 00100 virtual ~RefCounted() { if(exciseListeners) delete exciseListeners; } 00101 00102 /** This method is used by release() to destroy this object if the 00103 refcount reaches 0. By default, it simply calls "delete 00104 this". However, you can override it in a subclass if you 00105 really want to do something other than deleting this object. 00106 */ 00107 virtual void destroy(); 00108 00109 /** Increment the reference count. */ 00110 virtual void acquire(); 00111 00112 /** Decrement the reference count. The object will be deleted if (count == 0) */ 00113 virtual void release(); 00114 00115 /** Get the current reference count */ 00116 virtual int getRefCount(); 00117 00118 /** Add an excise listener. When the excise() method is called on 00119 this object, each excise listener will be notified so that it 00120 may clean up any references to this object. 00121 00122 @param oel the excise listener to call back. This can itself 00123 be a refcounted object, and if so, its refcount will be incremented. 00124 */ 00125 virtual void addExciseListener(ObjectExciseListener* oel); 00126 00127 /** Remove an excise listener. 00128 @see addExciseListener() 00129 @param oel the ObjectExciseListener to remove 00130 @param releaseIfRefcounted Should we test the object excise 00131 listener to see if it is refcounted, and if so release it? 00132 Under certain special circumstances (such as calling 00133 "removeExciseListener(this)" from a destructor) this behavior 00134 is undesirable. 00135 */ 00136 virtual void removeExciseListener(ObjectExciseListener* oel, 00137 bool releaseIfRefcounted = true); 00138 00139 /** Ask that all known references to this object to release 00140 their references so the object can be deleted, by calling 00141 ObjectExciseListener::notifyObjectExcise(). 00142 00143 @note This method is virtual and objects making use of this 00144 class will probably want to supply their own additional code 00145 to detach from linking data structures. Make sure to call 00146 RefCounted::excise() in yoru override method so that the 00147 excise listeners are called back. Overriding excise() will 00148 probably be sufficient for most uses; the purpose of the 00149 ObjectExciseListener facility is for plugin/application level 00150 hooks which may be beyond the scope of your immediate code. 00151 */ 00152 virtual void excise(); 00153 }; 00154 #endif 00155 00156 /** @class NullPointerError refcount.hh vos/vos/refcount.hh 00157 @ingroup libvutil 00158 00159 This is raised if you try and dereference a vRef which contains a 00160 null pointer. 00161 */ 00162 class VUTIL_API NullPointerError : public std::runtime_error { 00163 public: 00164 NullPointerError(const std::string& s) : runtime_error(s) { }; 00165 }; 00166 00167 00168 /** @class vRef refcount.hh vos/vos/refcount.hh 00169 @ingroup libvutil 00170 00171 This is a "smart pointer" wrapper class around any RefCounted 00172 object. When you assign a pointer to a vRef, it calls aquire(), 00173 increasing the refcount on that object. When vRef goes out of 00174 scope (i.e., when it is destroyed) it automatically calls release 00175 on the wrapped object. For more discussion and examples, see the 00176 VOS manual and the tutorial programs (in "apps/tutorials"). 00177 00178 @note vRef is not threadsafe. You must not share the same vRef 00179 instance between threads -- copy it instead! 00180 */ 00181 00182 template<class T> class vRef 00183 { 00184 private: 00185 T* ptr; 00186 00187 public: 00188 /** Initialize an empty vRef that points to nothing. */ 00189 vRef() : ptr(0) { } 00190 00191 /** @param x Pointer to object to wrap in this vRef: it must be a RefCounted 00192 class or subclass. 00193 @param doacquire If true, object will be acquired and reference count 00194 will be incremented. If false, the object will retain it's current 00195 reference count. Note that when a RefCounted object is created with 00196 "new" its reference count is already 1, and in this case you shouldn't 00197 increment the count. 00198 */ 00199 vRef(T* x, bool doacquire) : ptr(x) { if(ptr && doacquire) ptr->acquire(); } 00200 00201 /** @param x Reference to object to wrap in this vRef: it must be a 00202 RefCounted class or subclass. 00203 @param doacquire If true, object will be acquired and reference count 00204 will be incremented. If false, the object will retain it's current 00205 reference count. Note that when a RefCounted object is created with 00206 "new" its reference count is already 1, and in this case you shouldn't 00207 increment the count. 00208 00209 */ 00210 vRef(T& x, bool doacquire) : ptr(&x) { if(ptr && doacquire) ptr->acquire(); } 00211 00212 /** Create a new vRef containing the same object as another vRef. The 00213 reference count of the object will be incremented. 00214 @param x vRef to copy. 00215 */ 00216 vRef(const vRef<T>& x) : ptr(x.ptr) { if(ptr) ptr->acquire(); } 00217 00218 /** Create a new vRef containing the same object as another vRef. The 00219 reference count of the object will be incremented. This form 00220 allows the other vRef to use a different class in its template. 00221 @param x vRef to copy. 00222 */ 00223 template<class T2> vRef(const vRef<T2>& x) { 00224 if(x.isValid()) { 00225 ptr = x.get(); 00226 ptr->acquire(); 00227 } else ptr = 0; 00228 } 00229 00230 /** Destructor. Decrements the reference count on the underlying 00231 pointer. */ 00232 ~vRef() { if(ptr) ptr->release(); } 00233 00234 /** Test whether this vRef holds a valid pointer or not. 00235 @return true if internal pointer is not null (0), false if 00236 internal pointer is null (0). 00237 */ 00238 bool isValid() const { return (ptr != 0); } 00239 00240 #ifndef SWIG 00241 /** @return true if the wrapped pointer is null, false if it is 00242 valid */ 00243 bool operator!() const { return (ptr == 0); } 00244 00245 bool operator==(const vRef& x) const { return (x.ptr == ptr); } 00246 bool operator!=(const vRef& x) const { return (x.ptr != ptr); } 00247 00248 bool operator==(const T* x) const { return (x == ptr); } 00249 bool operator!=(const T* x) const { return (x != ptr); } 00250 00251 bool operator==(const T& x) const { return ( (ptr != 0) && (x == *ptr) ); } 00252 bool operator!=(const T& x) const { return ( (ptr != 0) && (x != *ptr) ); } 00253 00254 /** Assign a new object to this vRef. If this vRef is holding an object, 00255 that object will be released (it's reference count decremented). 00256 @param x the new pointer 00257 @param doacquire If true, object will be acquired and 00258 reference count will be incremented. If false, the object will 00259 retain it's current reference count. When a new RefCounted 00260 object is created (e.g. with "new"), its reference count is 00261 already 1, and you don't need to increment the count. 00262 */ 00263 template<class T2> vRef<T>& assign(T2* x, bool doacquire) { 00264 if(ptr) ptr->release(); 00265 ptr = x; 00266 if(ptr && doacquire) ptr->acquire(); 00267 return *this; 00268 } 00269 00270 /** Assign to another vRef. Old wrapped object is released, and new 00271 wrapped object is acquired. */ 00272 vRef<T>& operator=(const vRef<T>& x) { 00273 if(ptr) ptr->release(); 00274 ptr = x.ptr; 00275 if(ptr) ptr->acquire(); 00276 return *this; 00277 } 00278 00279 /** Assign to another vRef. Old wrapped object is released, and new 00280 wrapped object is acquired. */ 00281 template<class T2> vRef<T>& operator=(const vRef<T2>& x) { 00282 if(ptr) ptr->release(); 00283 if(x.isValid()) { 00284 ptr = x.get(); 00285 if(ptr) ptr->acquire(); 00286 } 00287 return *this; 00288 } 00289 00290 /** Return reference to wrapped object. 00291 @throw NullPointerError if internal pointer is 0 (no object held) 00292 */ 00293 T& operator*() const { 00294 if(ptr) return *ptr; 00295 else throw NullPointerError("tried to dereference null smart pointer in * operator"); 00296 } 00297 #endif 00298 00299 /** Return pointer to wrapped object. 00300 @throw NullPointerError if internal pointer is 0 (no object held) 00301 */ 00302 T* operator->() const { 00303 if(ptr) return ptr; 00304 else throw NullPointerError("tried to dereference null smart pointer in -> operator"); 00305 } 00306 00307 /** Return pointer to wrapped object 00308 @throw NullPointerError if internal pointer is 0 (no object held) 00309 */ 00310 T* get() const { 00311 if(ptr) return ptr; 00312 else throw NullPointerError("tried to dereference null smart pointer in & operator"); 00313 } 00314 00315 /** Release object and set internal pointer to 0. */ 00316 void clear() { 00317 if(ptr) ptr->release(); 00318 ptr = 0; 00319 } 00320 00321 00322 #ifndef SWIG 00323 00324 /** Cast to pointer. Same as get(). 00325 Usually used by implicit cast in a function call. 00326 @throw NullPointerError if internal pointer is 0 (no object held) 00327 */ 00328 operator T*() { 00329 if(ptr) return ptr; 00330 else throw NullPointerError("tried to dereference null smart pointer in cast"); 00331 } 00332 00333 /** Cast to reference. Same as the operator*(). 00334 Usually used by implicit cast in a function call. 00335 @throw NullPointerError if internal pointer is 0 (no object is held) 00336 */ 00337 operator T&() { 00338 if(ptr) return *ptr; 00339 else throw NullPointerError("tried to dereference null smart pointer in cast to reference"); 00340 } 00341 00342 #endif // ifndef SWIG 00343 00344 }; 00345 00346 } 00347 00348 #endif