Fawkes API  Fawkes Development Version
lockptr.h
1 
2 /***************************************************************************
3  * lockptr.h - refptr with user accessible lock
4  *
5  * Created: Sat Feb 26 15:27:39 2011
6  * Copyright 2002 The gtkmm Development Team
7  * 2005 The cairomm Development Team
8  * 2009-2011 Tim Niemueller [www.niemueller.de]
9  *
10  ****************************************************************************/
11 
12 /* This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version. A runtime exception applies to
16  * this software (see LICENSE.GPL_WRE file mentioned below for details).
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Library General Public License for more details.
22  *
23  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
24  */
25 
26 #ifndef __CORE_UTILS_LOCKPTR_H_
27 #define __CORE_UTILS_LOCKPTR_H_
28 
29 #include <core/utils/refptr.h>
30 #include <core/threading/mutex.h>
31 
32 namespace fawkes {
33 #if 0 /* just to make Emacs auto-indent happy */
34 }
35 #endif
36 
37 /** LockPtr<> is a reference-counting shared lockable smartpointer.
38  *
39  * Reference counting means that a shared reference count is incremented each
40  * time a LockPtr is copied, and decremented each time a LockPtr is destroyed,
41  * for instance when it leaves its scope. When the reference count reaches
42  * zero, the contained object is deleted
43  *
44  * Fawkes uses LockPtr so that you don't need to remember to delete
45  * the object explicitly, or know when a method expects you to delete
46  * the object that it returns.
47  *
48  * It is similar to RefPtr, but additionally it provides one shared lock which
49  * can be used to coordinate locking for the encapsulated object.
50  *
51  * Note that LockPtr is thread-safe, but that you need to handle locking and
52  * unlocking for the shared resource yourself!
53  *
54  * @ingroup FCL
55  */
56 template <class T_CppObject>
57 class LockPtr
58 {
59  public:
60  /** Default constructor
61  *
62  * Afterwards it will be null and use of -> will cause a segmentation fault.
63  */
64  inline LockPtr();
65 
66  /// Destructor - decrements reference count.
67  inline ~LockPtr();
68 
69  /** Constructor that takes ownership.
70  *
71  * This takes ownership of @a cpp_object, so it will be deleted when the
72  * last LockPtr is deleted, for instance when it goes out of scope.
73  * @param cpp_object C++ object to take ownership of
74  * @param recursive_mutex true to create a recursive mutex (with deadlock prevention
75  * when locked from the same thread) for the object mutex, false to create a normal
76  * mutex
77  * @see Mutex
78  */
79  explicit inline LockPtr(T_CppObject* cpp_object, bool recursive_mutex = false);
80 
81  /** Copy constructor
82  * This increments the shared reference count.
83  * @param src refptr to copy
84  */
85  inline LockPtr(const LockPtr<T_CppObject>& src);
86 
87  /** Copy constructor (from different, but castable type).
88  * Increments the reference count.
89  * @param src refptr to copy
90  */
91  template <class T_CastFrom>
92  inline LockPtr(const LockPtr<T_CastFrom>& src);
93 
94  /** Swap the contents of two LockPtr<>.
95  * This method swaps the internal pointers to T_CppObject. This can be
96  * done safely without involving a reference/unreference cycle and is
97  * therefore highly efficient.
98  * @param other other instance to swap with.
99  */
100  inline void swap(LockPtr<T_CppObject>& other);
101 
102  /** Copy from another LockPtr.
103  * @param src refptr to copy from
104  * @return reference to this instance
105  */
107 
108  /** Copy from different, but castable type).
109  * Increments the reference count.
110  * @param src refptr to copy from
111  * @return reference to this instance
112  */
113  template <class T_CastFrom>
115 
116  /** Assign object and claim ownership.
117  * @param ptr pointer to object, this refptr will claim ownership of the src!
118  * @return reference to this instance
119  */
120  inline LockPtr<T_CppObject>& operator=(T_CppObject *ptr);
121 
122 
123  /** Tests whether the LockPtr<> point to the same underlying instance.
124  * @param src refptr to compare to
125  * @return true if both refptrs point to the same instance.
126  */
127  inline bool operator==(const LockPtr<T_CppObject>& src) const;
128 
129  /** Tests whether the LockPtr<> do not point to the same underlying instance.
130  * @param src refptr to compare to
131  * @return true if both refptrs do not point to the same instance.
132  */
133  inline bool operator!=(const LockPtr<T_CppObject>& src) const;
134 
135  /** Dereferencing.
136  * Use the methods of the underlying instance like so:
137  * <code>refptr->memberfun()</code>.
138  * @return pointer to encapsulated object
139  */
140  inline T_CppObject* operator->() const;
141 
142  /** Get underlying pointer.
143  * Use with care!
144  * @return pointer to encapsulated object
145  */
146  inline T_CppObject* operator*() const;
147 
148  /** Test whether the LockPtr<> points to any underlying instance.
149  *
150  * Mimics usage of ordinary pointers:
151  * @code
152  * if (ptr)
153  * do_something();
154  * @endcode
155  */
156  inline operator bool() const;
157 
158  /// Set underlying instance to 0, decrementing reference count of existing instance appropriately.
159  inline void clear();
160 
161 
162  /** Dynamic cast to derived class.
163  *
164  * The LockPtr can't be cast with the usual notation so instead you can use
165  * @code
166  * ptr_derived = LockPtr<Derived>::cast_dynamic(ptr_base);
167  * @endcode
168  * @param src source refptr to cast
169  * @return refptr to object casted to given type
170  */
171  template <class T_CastFrom>
173  {
174  T_CppObject *const cpp_object = dynamic_cast<T_CppObject*>(src.operator->());
175 
176  if(cpp_object) //Check whether dynamic_cast<> succeeded so we don't pass a null object with a used refcount:
177  return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr());
178  else
179  return LockPtr<T_CppObject>();
180  }
181 
182  /** Static cast to derived class.
183  *
184  * Like the dynamic cast; the notation is
185  * @code
186  * ptr_derived = LockPtr<Derived>::cast_static(ptr_base);
187  * @endcode
188  * @param src source refptr to cast
189  * @return refptr to object casted to given type
190  */
191  template <class T_CastFrom>
193  {
194  T_CppObject *const cpp_object = static_cast<T_CppObject*>(src.operator->());
195 
196  return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr());
197  }
198 
199  /** Cast to non-const.
200  *
201  * The LockPtr can't be cast with the usual notation so instead you can use
202  * @code
203  * ptr_unconst = LockPtr<UnConstType>::cast_const(ptr_const);
204  * @endcode
205  * @param src source refptr to cast
206  * @return refptr to object casted to given type
207  */
208  template <class T_CastFrom>
210  {
211  T_CppObject *const cpp_object = const_cast<T_CppObject*>(src.operator->());
212 
213  return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr());
214  }
215 
216  /** For use only in the internal implementation of LockPtr.
217  * @param cpp_object C++ object to wrap
218  * @param objmutex object mutex
219  * @param refcount reference count
220  * @param refmutex reference count mutex
221  */
222  explicit inline LockPtr(T_CppObject *cpp_object, Mutex *objmutex,
223  int *refcount, Mutex *refmutex);
224 
225  /** Get current refcount.
226  * Get reference count. Use this with care, as it may change any time.
227  * @return current reference count
228  */
229  inline int refcount() const { return *__ref_count; }
230 
231  /** For use only in the internal implementation of sharedptr.
232  * Get reference count pointer.
233  * Warning: This is for internal use only. Do not manually modify the
234  * reference count with this pointer.
235  * @return pointer to refcount integer
236  */
237  inline int * refcount_ptr() const { return __ref_count; }
238 
239  /** For use only in the internal implementation of sharedptr.
240  * Get reference mutex.
241  * @return pointer to refcount mutex
242  */
243  inline Mutex * refmutex_ptr() const { return __ref_mutex; }
244 
245 
246  /** Lock access to the encapsulated object. */
247  void lock() const { __obj_mutex->lock(); };
248 
249  /** Try to acquire lock for the encapsulated object.
250  * @return true if the lock has been acquired, false otherwise
251  */
252  bool try_lock() const { return __obj_mutex->try_lock(); }
253 
254  /** Unlock object mutex. */
255  void unlock() const { __obj_mutex->unlock(); }
256 
257  /** Get object mutex.
258  * This is the same mutex that is used in the lock(), try_lock(),
259  * and unlock() methods.
260  * @return object mutex
261  */
262  inline Mutex * objmutex_ptr() const { return __obj_mutex; }
263 
264 private:
265  T_CppObject *__cpp_object;
266  mutable Mutex *__obj_mutex;
267  mutable int *__ref_count;
268  mutable Mutex *__ref_mutex;
269 };
270 
271 
272 // LockPtr<>::operator->() comes first here since it's used by other methods.
273 // If it would come after them it wouldn't be inlined.
274 
275 template <class T_CppObject> inline
277 {
278  return __cpp_object;
279 }
280 
281 template <class T_CppObject> inline
283 {
284  return __cpp_object;
285 }
286 
287 template <class T_CppObject> inline
289 :
290  __cpp_object(0),
291  __obj_mutex(0),
292  __ref_count(0),
293  __ref_mutex(0)
294 {}
295 
296 
297 template <class T_CppObject> inline
299 {
300  if(__ref_count && __ref_mutex)
301  {
302  __ref_mutex->lock();
303 
304  --(*__ref_count);
305 
306  if(*__ref_count == 0)
307  {
308  if(__cpp_object)
309  {
310  delete __cpp_object;
311  __cpp_object = 0;
312  }
313 
314  delete __ref_count;
315  delete __ref_mutex;
316  delete __obj_mutex;
317  __ref_count = 0;
318  __ref_mutex = 0;
319  } else {
320  __ref_mutex->unlock();
321  }
322  }
323 }
324 
325 
326 template <class T_CppObject> inline
327 LockPtr<T_CppObject>::LockPtr(T_CppObject* cpp_object, bool recursive_mutex)
328 : __cpp_object(cpp_object),
329  __obj_mutex(0),
330  __ref_count(0),
331  __ref_mutex(0)
332 {
333  if(cpp_object)
334  {
335  __ref_count = new int;
336  __ref_mutex = new Mutex(Mutex::RECURSIVE);
337  __obj_mutex = new Mutex(recursive_mutex ? Mutex::RECURSIVE : Mutex::NORMAL);
338  *__ref_count = 1; //This will be decremented in the destructor.
339  }
340 }
341 
342 //Used by cast_*() implementations:
343 template <class T_CppObject> inline
344 LockPtr<T_CppObject>::LockPtr(T_CppObject* cpp_object, Mutex *objmutex,
345  int* refcount, Mutex *refmutex)
346 : __cpp_object(cpp_object),
347  __obj_mutex(objmutex),
348  __ref_count(refcount),
349  __ref_mutex(refmutex)
350 {
351  if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) {
352  __ref_mutex->lock();
353  ++(*__ref_count);
354  __ref_mutex->unlock();
355  }
356 }
357 
358 template <class T_CppObject> inline
360 :
361  __cpp_object(src.__cpp_object),
362  __obj_mutex(src.__obj_mutex),
363  __ref_count(src.__ref_count),
364  __ref_mutex(src.__ref_mutex)
365 {
366  if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex)
367  {
368  __ref_mutex->lock();
369  ++(*__ref_count);
370  __ref_mutex->unlock();
371  }
372 }
373 
374 // The templated ctor allows copy construction from any object that's
375 // castable. Thus, it does downcasts:
376 // base_ref = derived_ref
377 template <class T_CppObject>
378  template <class T_CastFrom>
379 inline
381 :
382  // A different LockPtr<> will not allow us access to __cpp_object. We need
383  // to add a get_underlying() for this, but that would encourage incorrect
384  // use, so we use the less well-known operator->() accessor:
385  __cpp_object(src.operator->()),
386  __obj_mutex(src.objmutex_ptr()),
387  __ref_count(src.refcount_ptr()),
388  __ref_mutex(src.refmutex_ptr())
389 {
390  if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) {
391  __ref_mutex->lock();
392  ++(*__ref_count);
393  __ref_mutex->unlock();
394  }
395 }
396 
397 template <class T_CppObject> inline
398 void
400 {
401  T_CppObject *const temp = __cpp_object;
402  int *temp_count = __ref_count;
403  Mutex *temp_ref_mutex = __ref_mutex;
404  Mutex *temp_obj_mutex = __obj_mutex;
405 
406  __cpp_object = other.__cpp_object;
407  __obj_mutex = other.__obj_mutex;
408  __ref_count = other.__ref_count;
409  __ref_mutex = other.__ref_mutex;
410 
411  other.__cpp_object = temp;
412  other.__ref_count = temp_count;
413  other.__ref_mutex = temp_ref_mutex;
414  other.__obj_mutex = temp_obj_mutex;
415 }
416 
417 template <class T_CppObject> inline
420 {
421  // In case you haven't seen the swap() technique to implement copy
422  // assignment before, here's what it does:
423  //
424  // 1) Create a temporary LockPtr<> instance via the copy ctor, thereby
425  // increasing the reference count of the source object.
426  //
427  // 2) Swap the internal object pointers of *this and the temporary
428  // LockPtr<>. After this step, *this already contains the new pointer,
429  // and the old pointer is now managed by temp.
430  //
431  // 3) The destructor of temp is executed, thereby unreferencing the
432  // old object pointer.
433  //
434  // This technique is described in Herb Sutter's "Exceptional C++", and
435  // has a number of advantages over conventional approaches:
436  //
437  // - Code reuse by calling the copy ctor.
438  // - Strong exception safety for free.
439  // - Self assignment is handled implicitely.
440  // - Simplicity.
441  // - It just works and is hard to get wrong; i.e. you can use it without
442  // even thinking about it to implement copy assignment whereever the
443  // object data is managed indirectly via a pointer, which is very common.
444 
445  LockPtr<T_CppObject> temp (src);
446  this->swap(temp);
447  return *this;
448 }
449 
450 template <class T_CppObject> inline
453 {
454  LockPtr<T_CppObject> temp(ptr);
455  this->swap(temp);
456  return *this;
457 }
458 
459 
460 template <class T_CppObject>
461  template <class T_CastFrom>
462 inline
465 {
466  LockPtr<T_CppObject> temp (src);
467  this->swap(temp);
468  return *this;
469 }
470 
471 template <class T_CppObject> inline
472 bool
474 {
475  return (__cpp_object == src.__cpp_object);
476 }
477 
478 template <class T_CppObject> inline
479 bool
481 {
482  return (__cpp_object != src.__cpp_object);
483 }
484 
485 template <class T_CppObject> inline
487 {
488  return (__cpp_object != 0);
489 }
490 
491 template <class T_CppObject> inline
493 {
494  LockPtr<T_CppObject> temp; // swap with an empty LockPtr<> to clear *this
495  this->swap(temp);
496 }
497 
498 
499 /** Swap refptr instances.
500  * @param lrp "left" refptr
501  * @param rrp "right" refptr
502  * @relates fawkes::LockPtr
503  */
504 template <class T_CppObject> inline
505 void
507 {
508  lrp.swap(rrp);
509 }
510 
511 } // end namespace fawkes
512 
513 #endif
bool operator!=(const LockPtr< T_CppObject > &src) const
Tests whether the LockPtr<> do not point to the same underlying instance.
Definition: lockptr.h:480
int * refcount_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:237
Mutex * refmutex_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:243
Fawkes library namespace.
void unlock()
Unlock the mutex.
Definition: mutex.cpp:135
T_CppObject * operator*() const
Get underlying pointer.
Definition: lockptr.h:282
void unlock() const
Unlock object mutex.
Definition: lockptr.h:255
T_CppObject * operator->() const
Dereferencing.
Definition: lockptr.h:276
LockPtr< T_CppObject > & operator=(const LockPtr< T_CppObject > &src)
Copy from another LockPtr.
Definition: lockptr.h:419
LockPtr<> is a reference-counting shared lockable smartpointer.
Definition: lockptr.h:57
bool try_lock() const
Try to acquire lock for the encapsulated object.
Definition: lockptr.h:252
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:262
bool operator==(const LockPtr< T_CppObject > &src) const
Tests whether the LockPtr<> point to the same underlying instance.
Definition: lockptr.h:473
~LockPtr()
Destructor - decrements reference count.
Definition: lockptr.h:298
static LockPtr< T_CppObject > cast_dynamic(const LockPtr< T_CastFrom > &src)
Dynamic cast to derived class.
Definition: lockptr.h:172
bool try_lock()
Tries to lock the mutex.
Definition: mutex.cpp:120
void clear()
Set underlying instance to 0, decrementing reference count of existing instance appropriately.
Definition: lockptr.h:492
void swap(LockPtr< T_CppObject > &lrp, LockPtr< T_CppObject > &rrp)
Swap refptr instances.
Definition: lockptr.h:506
static LockPtr< T_CppObject > cast_const(const LockPtr< T_CastFrom > &src)
Cast to non-const.
Definition: lockptr.h:209
LockPtr()
Default constructor.
Definition: lockptr.h:288
void lock()
Lock this mutex.
Definition: mutex.cpp:89
Mutex mutual exclusion lock.
Definition: mutex.h:32
void swap(LockPtr< T_CppObject > &other)
Swap the contents of two LockPtr<>.
Definition: lockptr.h:399
static LockPtr< T_CppObject > cast_static(const LockPtr< T_CastFrom > &src)
Static cast to derived class.
Definition: lockptr.h:192
void lock() const
Lock access to the encapsulated object.
Definition: lockptr.h:247
int refcount() const
Get current refcount.
Definition: lockptr.h:229