Fawkes API  Fawkes Development Version
lockptr.h
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