Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * lockptr.h - refptr with user accessible lock 00004 * 00005 * Created: Sat Feb 26 15:27:39 2011 00006 * Copyright 2002 The gtkmm Development Team 00007 * 2005 The cairomm Development Team 00008 * 2009-2011 Tim Niemueller [www.niemueller.de] 00009 * 00010 ****************************************************************************/ 00011 00012 /* This program is free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2 of the License, or 00015 * (at your option) any later version. A runtime exception applies to 00016 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00017 * 00018 * This program is distributed in the hope that it will be useful, 00019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00021 * GNU Library General Public License for more details. 00022 * 00023 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00024 */ 00025 00026 #ifndef __CORE_UTILS_LOCKPTR_H_ 00027 #define __CORE_UTILS_LOCKPTR_H_ 00028 00029 #include <core/utils/refptr.h> 00030 #include <core/threading/mutex.h> 00031 00032 namespace fawkes { 00033 #if 0 /* just to make Emacs auto-indent happy */ 00034 } 00035 #endif 00036 00037 /** LockPtr<> is a reference-counting shared lockable smartpointer. 00038 * 00039 * Reference counting means that a shared reference count is incremented each 00040 * time a LockPtr is copied, and decremented each time a LockPtr is destroyed, 00041 * for instance when it leaves its scope. When the reference count reaches 00042 * zero, the contained object is deleted 00043 * 00044 * Fawkes uses LockPtr so that you don't need to remember to delete 00045 * the object explicitly, or know when a method expects you to delete 00046 * the object that it returns. 00047 * 00048 * It is similar to RefPtr, but additionally it provides one shared lock which 00049 * can be used to coordinate locking for the encapsulated object. 00050 * 00051 * Note that LockPtr is thread-safe, but that you need to handle locking and 00052 * unlocking for the shared resource yourself! 00053 * 00054 * @ingroup FCL 00055 */ 00056 template <class T_CppObject> 00057 class LockPtr 00058 { 00059 public: 00060 /** Default constructor 00061 * 00062 * Afterwards it will be null and use of -> will cause a segmentation fault. 00063 */ 00064 inline LockPtr(); 00065 00066 /// Destructor - decrements reference count. 00067 inline ~LockPtr(); 00068 00069 /** Constructor that takes ownership. 00070 * 00071 * This takes ownership of @a cpp_object, so it will be deleted when the 00072 * last LockPtr is deleted, for instance when it goes out of scope. 00073 * @param cpp_object C++ object to take ownership of 00074 */ 00075 explicit inline LockPtr(T_CppObject* cpp_object); 00076 00077 /** Copy constructor 00078 * This increments the shared reference count. 00079 * @param src refptr to copy 00080 */ 00081 inline LockPtr(const LockPtr<T_CppObject>& src); 00082 00083 /** Copy constructor (from different, but castable type). 00084 * Increments the reference count. 00085 * @param src refptr to copy 00086 */ 00087 template <class T_CastFrom> 00088 inline LockPtr(const LockPtr<T_CastFrom>& src); 00089 00090 /** Swap the contents of two LockPtr<>. 00091 * This method swaps the internal pointers to T_CppObject. This can be 00092 * done safely without involving a reference/unreference cycle and is 00093 * therefore highly efficient. 00094 * @param other other instance to swap with. 00095 */ 00096 inline void swap(LockPtr<T_CppObject>& other); 00097 00098 /** Copy from another LockPtr. 00099 * @param src refptr to copy from 00100 * @return reference to this instance 00101 */ 00102 inline LockPtr<T_CppObject>& operator=(const LockPtr<T_CppObject>& src); 00103 00104 /** Copy from different, but castable type). 00105 * Increments the reference count. 00106 * @param src refptr to copy from 00107 * @return reference to this instance 00108 */ 00109 template <class T_CastFrom> 00110 inline LockPtr<T_CppObject>& operator=(const LockPtr<T_CastFrom>& src); 00111 00112 /** Assign object and claim ownership. 00113 * @param ptr pointer to object, this refptr will claim ownership of the src! 00114 * @return reference to this instance 00115 */ 00116 inline LockPtr<T_CppObject>& operator=(T_CppObject *ptr); 00117 00118 00119 /** Tests whether the LockPtr<> point to the same underlying instance. 00120 * @param src refptr to compare to 00121 * @return true if both refptrs point to the same instance. 00122 */ 00123 inline bool operator==(const LockPtr<T_CppObject>& src) const; 00124 00125 /** Tests whether the LockPtr<> do not point to the same underlying instance. 00126 * @param src refptr to compare to 00127 * @return true if both refptrs do not point to the same instance. 00128 */ 00129 inline bool operator!=(const LockPtr<T_CppObject>& src) const; 00130 00131 /** Dereferencing. 00132 * Use the methods of the underlying instance like so: 00133 * <code>refptr->memberfun()</code>. 00134 * @return pointer to encapsulated object 00135 */ 00136 inline T_CppObject* operator->() const; 00137 00138 /** Get underlying pointer. 00139 * Use with care! 00140 * @return pointer to encapsulated object 00141 */ 00142 inline T_CppObject* operator*() const; 00143 00144 /** Test whether the LockPtr<> points to any underlying instance. 00145 * 00146 * Mimics usage of ordinary pointers: 00147 * @code 00148 * if (ptr) 00149 * do_something(); 00150 * @endcode 00151 */ 00152 inline operator bool() const; 00153 00154 /// Set underlying instance to 0, decrementing reference count of existing instance appropriately. 00155 inline void clear(); 00156 00157 00158 /** Dynamic cast to derived class. 00159 * 00160 * The LockPtr can't be cast with the usual notation so instead you can use 00161 * @code 00162 * ptr_derived = LockPtr<Derived>::cast_dynamic(ptr_base); 00163 * @endcode 00164 * @param src source refptr to cast 00165 * @return refptr to object casted to given type 00166 */ 00167 template <class T_CastFrom> 00168 static inline LockPtr<T_CppObject> cast_dynamic(const LockPtr<T_CastFrom>& src); 00169 00170 /** Static cast to derived class. 00171 * 00172 * Like the dynamic cast; the notation is 00173 * @code 00174 * ptr_derived = LockPtr<Derived>::cast_static(ptr_base); 00175 * @endcode 00176 * @param src source refptr to cast 00177 * @return refptr to object casted to given type 00178 */ 00179 template <class T_CastFrom> 00180 static inline LockPtr<T_CppObject> cast_static(const LockPtr<T_CastFrom>& src); 00181 00182 /** Cast to non-const. 00183 * 00184 * The LockPtr can't be cast with the usual notation so instead you can use 00185 * @code 00186 * ptr_unconst = LockPtr<UnConstType>::cast_const(ptr_const); 00187 * @endcode 00188 * @param src source refptr to cast 00189 * @return refptr to object casted to given type 00190 */ 00191 template <class T_CastFrom> 00192 static inline LockPtr<T_CppObject> cast_const(const LockPtr<T_CastFrom>& src); 00193 00194 /** For use only in the internal implementation of LockPtr. 00195 * @param cpp_object C++ object to wrap 00196 * @param objmutex object mutex 00197 * @param refcount reference count 00198 * @param refmutex reference count mutex 00199 */ 00200 explicit inline LockPtr(T_CppObject *cpp_object, Mutex *objmutex, 00201 int *refcount, Mutex *refmutex); 00202 00203 /** Get current refcount. 00204 * Get reference count. Use this with care, as it may change any time. 00205 * @return current reference count 00206 */ 00207 inline int refcount() const { return *__ref_count; } 00208 00209 /** For use only in the internal implementation of sharedptr. 00210 * Get reference count pointer. 00211 * Warning: This is for internal use only. Do not manually modify the 00212 * reference count with this pointer. 00213 * @return pointer to refcount integer 00214 */ 00215 inline int * refcount_ptr() const { return __ref_count; } 00216 00217 /** For use only in the internal implementation of sharedptr. 00218 * Get reference mutex. 00219 * @return pointer to refcount mutex 00220 */ 00221 inline Mutex * refmutex_ptr() const { return __ref_mutex; } 00222 00223 00224 /** Lock access to the encapsulated object. */ 00225 void lock() const { __obj_mutex->lock(); }; 00226 00227 /** Try to acquire lock for the encapsulated object. 00228 * @return true if the lock has been acquired, false otherwise 00229 */ 00230 bool try_lock() const { return __obj_mutex->try_lock(); } 00231 00232 /** Unlock object mutex. */ 00233 void unlock() const { __obj_mutex->unlock(); } 00234 00235 /** Get object mutex. 00236 * This is the same mutex that is used in the lock(), try_lock(), 00237 * and unlock() methods. 00238 * @return object mutex 00239 */ 00240 inline Mutex * objmutex_ptr() const { return __obj_mutex; } 00241 00242 private: 00243 T_CppObject *__cpp_object; 00244 mutable Mutex *__obj_mutex; 00245 mutable int *__ref_count; 00246 mutable Mutex *__ref_mutex; 00247 }; 00248 00249 00250 // LockPtr<>::operator->() comes first here since it's used by other methods. 00251 // If it would come after them it wouldn't be inlined. 00252 00253 template <class T_CppObject> inline 00254 T_CppObject* LockPtr<T_CppObject>::operator->() const 00255 { 00256 return __cpp_object; 00257 } 00258 00259 template <class T_CppObject> inline 00260 T_CppObject* LockPtr<T_CppObject>::operator*() const 00261 { 00262 return __cpp_object; 00263 } 00264 00265 template <class T_CppObject> inline 00266 LockPtr<T_CppObject>::LockPtr() 00267 : 00268 __cpp_object(0), 00269 __obj_mutex(0), 00270 __ref_count(0), 00271 __ref_mutex(0) 00272 {} 00273 00274 00275 template <class T_CppObject> inline 00276 LockPtr<T_CppObject>::~LockPtr() 00277 { 00278 if(__ref_count && __ref_mutex) 00279 { 00280 __ref_mutex->lock(); 00281 00282 --(*__ref_count); 00283 00284 if(*__ref_count == 0) 00285 { 00286 if(__cpp_object) 00287 { 00288 delete __cpp_object; 00289 __cpp_object = 0; 00290 } 00291 00292 delete __ref_count; 00293 delete __ref_mutex; 00294 delete __obj_mutex; 00295 __ref_count = 0; 00296 __ref_mutex = 0; 00297 } else { 00298 __ref_mutex->unlock(); 00299 } 00300 } 00301 } 00302 00303 00304 template <class T_CppObject> inline 00305 LockPtr<T_CppObject>::LockPtr(T_CppObject* cpp_object) 00306 : __cpp_object(cpp_object), 00307 __obj_mutex(0), 00308 __ref_count(0), 00309 __ref_mutex(0) 00310 { 00311 if(cpp_object) 00312 { 00313 __ref_count = new int; 00314 __ref_mutex = new Mutex(); 00315 __obj_mutex = new Mutex(); 00316 *__ref_count = 1; //This will be decremented in the destructor. 00317 } 00318 } 00319 00320 //Used by cast_*() implementations: 00321 template <class T_CppObject> inline 00322 LockPtr<T_CppObject>::LockPtr(T_CppObject* cpp_object, Mutex *objmutex, 00323 int* refcount, Mutex *refmutex) 00324 : __cpp_object(cpp_object), 00325 __obj_mutex(objmutex), 00326 __ref_count(refcount), 00327 __ref_mutex(refmutex) 00328 { 00329 if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) { 00330 __ref_mutex->lock(); 00331 ++(*__ref_count); 00332 __ref_mutex->unlock(); 00333 } 00334 } 00335 00336 template <class T_CppObject> inline 00337 LockPtr<T_CppObject>::LockPtr(const LockPtr<T_CppObject>& src) 00338 : 00339 __cpp_object(src.__cpp_object), 00340 __obj_mutex(src.__obj_mutex), 00341 __ref_count(src.__ref_count), 00342 __ref_mutex(src.__ref_mutex) 00343 { 00344 if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) 00345 { 00346 __ref_mutex->lock(); 00347 ++(*__ref_count); 00348 __ref_mutex->unlock(); 00349 } 00350 } 00351 00352 // The templated ctor allows copy construction from any object that's 00353 // castable. Thus, it does downcasts: 00354 // base_ref = derived_ref 00355 template <class T_CppObject> 00356 template <class T_CastFrom> 00357 inline 00358 LockPtr<T_CppObject>::LockPtr(const LockPtr<T_CastFrom>& src) 00359 : 00360 // A different LockPtr<> will not allow us access to __cpp_object. We need 00361 // to add a get_underlying() for this, but that would encourage incorrect 00362 // use, so we use the less well-known operator->() accessor: 00363 __cpp_object(src.operator->()), 00364 __obj_mutex(src.objmutex_ptr()), 00365 __ref_count(src.refcount_ptr()), 00366 __ref_mutex(src.refmutex_ptr()) 00367 { 00368 if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) { 00369 __ref_mutex->lock(); 00370 ++(*__ref_count); 00371 __ref_mutex->unlock(); 00372 } 00373 } 00374 00375 template <class T_CppObject> inline 00376 void 00377 LockPtr<T_CppObject>::swap(LockPtr<T_CppObject>& other) 00378 { 00379 T_CppObject *const temp = __cpp_object; 00380 int *temp_count = __ref_count; 00381 Mutex *temp_ref_mutex = __ref_mutex; 00382 Mutex *temp_obj_mutex = __obj_mutex; 00383 00384 __cpp_object = other.__cpp_object; 00385 __obj_mutex = other.__obj_mutex; 00386 __ref_count = other.__ref_count; 00387 __ref_mutex = other.__ref_mutex; 00388 00389 other.__cpp_object = temp; 00390 other.__ref_count = temp_count; 00391 other.__ref_mutex = temp_ref_mutex; 00392 other.__obj_mutex = temp_obj_mutex; 00393 } 00394 00395 template <class T_CppObject> inline 00396 LockPtr<T_CppObject>& 00397 LockPtr<T_CppObject>::operator=(const LockPtr<T_CppObject>& src) 00398 { 00399 // In case you haven't seen the swap() technique to implement copy 00400 // assignment before, here's what it does: 00401 // 00402 // 1) Create a temporary LockPtr<> instance via the copy ctor, thereby 00403 // increasing the reference count of the source object. 00404 // 00405 // 2) Swap the internal object pointers of *this and the temporary 00406 // LockPtr<>. After this step, *this already contains the new pointer, 00407 // and the old pointer is now managed by temp. 00408 // 00409 // 3) The destructor of temp is executed, thereby unreferencing the 00410 // old object pointer. 00411 // 00412 // This technique is described in Herb Sutter's "Exceptional C++", and 00413 // has a number of advantages over conventional approaches: 00414 // 00415 // - Code reuse by calling the copy ctor. 00416 // - Strong exception safety for free. 00417 // - Self assignment is handled implicitely. 00418 // - Simplicity. 00419 // - It just works and is hard to get wrong; i.e. you can use it without 00420 // even thinking about it to implement copy assignment whereever the 00421 // object data is managed indirectly via a pointer, which is very common. 00422 00423 LockPtr<T_CppObject> temp (src); 00424 this->swap(temp); 00425 return *this; 00426 } 00427 00428 template <class T_CppObject> inline 00429 LockPtr<T_CppObject>& 00430 LockPtr<T_CppObject>::operator=(T_CppObject *ptr) 00431 { 00432 LockPtr<T_CppObject> temp(ptr); 00433 this->swap(temp); 00434 return *this; 00435 } 00436 00437 00438 template <class T_CppObject> 00439 template <class T_CastFrom> 00440 inline 00441 LockPtr<T_CppObject>& 00442 LockPtr<T_CppObject>::operator=(const LockPtr<T_CastFrom>& src) 00443 { 00444 LockPtr<T_CppObject> temp (src); 00445 this->swap(temp); 00446 return *this; 00447 } 00448 00449 template <class T_CppObject> inline 00450 bool 00451 LockPtr<T_CppObject>::operator==(const LockPtr<T_CppObject>& src) const 00452 { 00453 return (__cpp_object == src.__cpp_object); 00454 } 00455 00456 template <class T_CppObject> inline 00457 bool 00458 LockPtr<T_CppObject>::operator!=(const LockPtr<T_CppObject>& src) const 00459 { 00460 return (__cpp_object != src.__cpp_object); 00461 } 00462 00463 template <class T_CppObject> inline 00464 LockPtr<T_CppObject>::operator bool() const 00465 { 00466 return (__cpp_object != 0); 00467 } 00468 00469 template <class T_CppObject> inline 00470 void LockPtr<T_CppObject>::clear() 00471 { 00472 LockPtr<T_CppObject> temp; // swap with an empty LockPtr<> to clear *this 00473 this->swap(temp); 00474 } 00475 00476 template <class T_CppObject> 00477 template <class T_CastFrom> 00478 inline 00479 LockPtr<T_CppObject> 00480 LockPtr<T_CppObject>::cast_dynamic(const LockPtr<T_CastFrom>& src) 00481 { 00482 T_CppObject *const cpp_object = dynamic_cast<T_CppObject*>(src.operator->()); 00483 00484 if(cpp_object) //Check whether dynamic_cast<> succeeded so we don't pass a null object with a used refcount: 00485 return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr()); 00486 else 00487 return LockPtr<T_CppObject>(); 00488 } 00489 00490 template <class T_CppObject> 00491 template <class T_CastFrom> 00492 inline 00493 LockPtr<T_CppObject> 00494 LockPtr<T_CppObject>::cast_static(const LockPtr<T_CastFrom>& src) 00495 { 00496 T_CppObject *const cpp_object = static_cast<T_CppObject*>(src.operator->()); 00497 00498 return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr()); 00499 } 00500 00501 template <class T_CppObject> 00502 template <class T_CastFrom> 00503 inline 00504 LockPtr<T_CppObject> 00505 LockPtr<T_CppObject>::cast_const(const LockPtr<T_CastFrom>& src) 00506 { 00507 T_CppObject *const cpp_object = const_cast<T_CppObject*>(src.operator->()); 00508 00509 return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr()); 00510 } 00511 00512 00513 /** Swap refptr instances. 00514 * @param lrp "left" refptr 00515 * @param rrp "right" refptr 00516 * @relates fawkes::LockPtr 00517 */ 00518 template <class T_CppObject> inline 00519 void 00520 swap(LockPtr<T_CppObject>& lrp, LockPtr<T_CppObject>& rrp) 00521 { 00522 lrp.swap(rrp); 00523 } 00524 00525 } // end namespace fawkes 00526 00527 #endif