Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
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  */
75  explicit inline LockPtr(T_CppObject* cpp_object);
76 
77  /** Copy constructor
78  * This increments the shared reference count.
79  * @param src refptr to copy
80  */
81  inline LockPtr(const LockPtr<T_CppObject>& src);
82 
83  /** Copy constructor (from different, but castable type).
84  * Increments the reference count.
85  * @param src refptr to copy
86  */
87  template <class T_CastFrom>
88  inline LockPtr(const LockPtr<T_CastFrom>& src);
89 
90  /** Swap the contents of two LockPtr<>.
91  * This method swaps the internal pointers to T_CppObject. This can be
92  * done safely without involving a reference/unreference cycle and is
93  * therefore highly efficient.
94  * @param other other instance to swap with.
95  */
96  inline void swap(LockPtr<T_CppObject>& other);
97 
98  /** Copy from another LockPtr.
99  * @param src refptr to copy from
100  * @return reference to this instance
101  */
102  inline LockPtr<T_CppObject>& operator=(const LockPtr<T_CppObject>& src);
103 
104  /** Copy from different, but castable type).
105  * Increments the reference count.
106  * @param src refptr to copy from
107  * @return reference to this instance
108  */
109  template <class T_CastFrom>
110  inline LockPtr<T_CppObject>& operator=(const LockPtr<T_CastFrom>& src);
111 
112  /** Assign object and claim ownership.
113  * @param ptr pointer to object, this refptr will claim ownership of the src!
114  * @return reference to this instance
115  */
116  inline LockPtr<T_CppObject>& operator=(T_CppObject *ptr);
117 
118 
119  /** Tests whether the LockPtr<> point to the same underlying instance.
120  * @param src refptr to compare to
121  * @return true if both refptrs point to the same instance.
122  */
123  inline bool operator==(const LockPtr<T_CppObject>& src) const;
124 
125  /** Tests whether the LockPtr<> do not point to the same underlying instance.
126  * @param src refptr to compare to
127  * @return true if both refptrs do not point to the same instance.
128  */
129  inline bool operator!=(const LockPtr<T_CppObject>& src) const;
130 
131  /** Dereferencing.
132  * Use the methods of the underlying instance like so:
133  * <code>refptr->memberfun()</code>.
134  * @return pointer to encapsulated object
135  */
136  inline T_CppObject* operator->() const;
137 
138  /** Get underlying pointer.
139  * Use with care!
140  * @return pointer to encapsulated object
141  */
142  inline T_CppObject* operator*() const;
143 
144  /** Test whether the LockPtr<> points to any underlying instance.
145  *
146  * Mimics usage of ordinary pointers:
147  * @code
148  * if (ptr)
149  * do_something();
150  * @endcode
151  */
152  inline operator bool() const;
153 
154  /// Set underlying instance to 0, decrementing reference count of existing instance appropriately.
155  inline void clear();
156 
157 
158  /** Dynamic cast to derived class.
159  *
160  * The LockPtr can't be cast with the usual notation so instead you can use
161  * @code
162  * ptr_derived = LockPtr<Derived>::cast_dynamic(ptr_base);
163  * @endcode
164  * @param src source refptr to cast
165  * @return refptr to object casted to given type
166  */
167  template <class T_CastFrom>
168  static inline LockPtr<T_CppObject> cast_dynamic(const LockPtr<T_CastFrom>& src);
169 
170  /** Static cast to derived class.
171  *
172  * Like the dynamic cast; the notation is
173  * @code
174  * ptr_derived = LockPtr<Derived>::cast_static(ptr_base);
175  * @endcode
176  * @param src source refptr to cast
177  * @return refptr to object casted to given type
178  */
179  template <class T_CastFrom>
180  static inline LockPtr<T_CppObject> cast_static(const LockPtr<T_CastFrom>& src);
181 
182  /** Cast to non-const.
183  *
184  * The LockPtr can't be cast with the usual notation so instead you can use
185  * @code
186  * ptr_unconst = LockPtr<UnConstType>::cast_const(ptr_const);
187  * @endcode
188  * @param src source refptr to cast
189  * @return refptr to object casted to given type
190  */
191  template <class T_CastFrom>
192  static inline LockPtr<T_CppObject> cast_const(const LockPtr<T_CastFrom>& src);
193 
194  /** For use only in the internal implementation of LockPtr.
195  * @param cpp_object C++ object to wrap
196  * @param objmutex object mutex
197  * @param refcount reference count
198  * @param refmutex reference count mutex
199  */
200  explicit inline LockPtr(T_CppObject *cpp_object, Mutex *objmutex,
201  int *refcount, Mutex *refmutex);
202 
203  /** Get current refcount.
204  * Get reference count. Use this with care, as it may change any time.
205  * @return current reference count
206  */
207  inline int refcount() const { return *__ref_count; }
208 
209  /** For use only in the internal implementation of sharedptr.
210  * Get reference count pointer.
211  * Warning: This is for internal use only. Do not manually modify the
212  * reference count with this pointer.
213  * @return pointer to refcount integer
214  */
215  inline int * refcount_ptr() const { return __ref_count; }
216 
217  /** For use only in the internal implementation of sharedptr.
218  * Get reference mutex.
219  * @return pointer to refcount mutex
220  */
221  inline Mutex * refmutex_ptr() const { return __ref_mutex; }
222 
223 
224  /** Lock access to the encapsulated object. */
225  void lock() const { __obj_mutex->lock(); };
226 
227  /** Try to acquire lock for the encapsulated object.
228  * @return true if the lock has been acquired, false otherwise
229  */
230  bool try_lock() const { return __obj_mutex->try_lock(); }
231 
232  /** Unlock object mutex. */
233  void unlock() const { __obj_mutex->unlock(); }
234 
235  /** Get object mutex.
236  * This is the same mutex that is used in the lock(), try_lock(),
237  * and unlock() methods.
238  * @return object mutex
239  */
240  inline Mutex * objmutex_ptr() const { return __obj_mutex; }
241 
242 private:
243  T_CppObject *__cpp_object;
244  mutable Mutex *__obj_mutex;
245  mutable int *__ref_count;
246  mutable Mutex *__ref_mutex;
247 };
248 
249 
250 // LockPtr<>::operator->() comes first here since it's used by other methods.
251 // If it would come after them it wouldn't be inlined.
252 
253 template <class T_CppObject> inline
255 {
256  return __cpp_object;
257 }
258 
259 template <class T_CppObject> inline
261 {
262  return __cpp_object;
263 }
264 
265 template <class T_CppObject> inline
267 :
268  __cpp_object(0),
269  __obj_mutex(0),
270  __ref_count(0),
271  __ref_mutex(0)
272 {}
273 
274 
275 template <class T_CppObject> inline
277 {
278  if(__ref_count && __ref_mutex)
279  {
280  __ref_mutex->lock();
281 
282  --(*__ref_count);
283 
284  if(*__ref_count == 0)
285  {
286  if(__cpp_object)
287  {
288  delete __cpp_object;
289  __cpp_object = 0;
290  }
291 
292  delete __ref_count;
293  delete __ref_mutex;
294  delete __obj_mutex;
295  __ref_count = 0;
296  __ref_mutex = 0;
297  } else {
298  __ref_mutex->unlock();
299  }
300  }
301 }
302 
303 
304 template <class T_CppObject> inline
305 LockPtr<T_CppObject>::LockPtr(T_CppObject* cpp_object)
306 : __cpp_object(cpp_object),
307  __obj_mutex(0),
308  __ref_count(0),
309  __ref_mutex(0)
310 {
311  if(cpp_object)
312  {
313  __ref_count = new int;
314  __ref_mutex = new Mutex();
315  __obj_mutex = new Mutex();
316  *__ref_count = 1; //This will be decremented in the destructor.
317  }
318 }
319 
320 //Used by cast_*() implementations:
321 template <class T_CppObject> inline
322 LockPtr<T_CppObject>::LockPtr(T_CppObject* cpp_object, Mutex *objmutex,
323  int* refcount, Mutex *refmutex)
324 : __cpp_object(cpp_object),
325  __obj_mutex(objmutex),
326  __ref_count(refcount),
327  __ref_mutex(refmutex)
328 {
329  if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) {
330  __ref_mutex->lock();
331  ++(*__ref_count);
332  __ref_mutex->unlock();
333  }
334 }
335 
336 template <class T_CppObject> inline
338 :
339  __cpp_object(src.__cpp_object),
340  __obj_mutex(src.__obj_mutex),
341  __ref_count(src.__ref_count),
342  __ref_mutex(src.__ref_mutex)
343 {
344  if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex)
345  {
346  __ref_mutex->lock();
347  ++(*__ref_count);
348  __ref_mutex->unlock();
349  }
350 }
351 
352 // The templated ctor allows copy construction from any object that's
353 // castable. Thus, it does downcasts:
354 // base_ref = derived_ref
355 template <class T_CppObject>
356  template <class T_CastFrom>
357 inline
359 :
360  // A different LockPtr<> will not allow us access to __cpp_object. We need
361  // to add a get_underlying() for this, but that would encourage incorrect
362  // use, so we use the less well-known operator->() accessor:
363  __cpp_object(src.operator->()),
364  __obj_mutex(src.objmutex_ptr()),
365  __ref_count(src.refcount_ptr()),
366  __ref_mutex(src.refmutex_ptr())
367 {
368  if(__cpp_object && __obj_mutex && __ref_count && __ref_mutex) {
369  __ref_mutex->lock();
370  ++(*__ref_count);
371  __ref_mutex->unlock();
372  }
373 }
374 
375 template <class T_CppObject> inline
376 void
378 {
379  T_CppObject *const temp = __cpp_object;
380  int *temp_count = __ref_count;
381  Mutex *temp_ref_mutex = __ref_mutex;
382  Mutex *temp_obj_mutex = __obj_mutex;
383 
384  __cpp_object = other.__cpp_object;
385  __obj_mutex = other.__obj_mutex;
386  __ref_count = other.__ref_count;
387  __ref_mutex = other.__ref_mutex;
388 
389  other.__cpp_object = temp;
390  other.__ref_count = temp_count;
391  other.__ref_mutex = temp_ref_mutex;
392  other.__obj_mutex = temp_obj_mutex;
393 }
394 
395 template <class T_CppObject> inline
398 {
399  // In case you haven't seen the swap() technique to implement copy
400  // assignment before, here's what it does:
401  //
402  // 1) Create a temporary LockPtr<> instance via the copy ctor, thereby
403  // increasing the reference count of the source object.
404  //
405  // 2) Swap the internal object pointers of *this and the temporary
406  // LockPtr<>. After this step, *this already contains the new pointer,
407  // and the old pointer is now managed by temp.
408  //
409  // 3) The destructor of temp is executed, thereby unreferencing the
410  // old object pointer.
411  //
412  // This technique is described in Herb Sutter's "Exceptional C++", and
413  // has a number of advantages over conventional approaches:
414  //
415  // - Code reuse by calling the copy ctor.
416  // - Strong exception safety for free.
417  // - Self assignment is handled implicitely.
418  // - Simplicity.
419  // - It just works and is hard to get wrong; i.e. you can use it without
420  // even thinking about it to implement copy assignment whereever the
421  // object data is managed indirectly via a pointer, which is very common.
422 
423  LockPtr<T_CppObject> temp (src);
424  this->swap(temp);
425  return *this;
426 }
427 
428 template <class T_CppObject> inline
431 {
432  LockPtr<T_CppObject> temp(ptr);
433  this->swap(temp);
434  return *this;
435 }
436 
437 
438 template <class T_CppObject>
439  template <class T_CastFrom>
440 inline
443 {
444  LockPtr<T_CppObject> temp (src);
445  this->swap(temp);
446  return *this;
447 }
448 
449 template <class T_CppObject> inline
450 bool
452 {
453  return (__cpp_object == src.__cpp_object);
454 }
455 
456 template <class T_CppObject> inline
457 bool
459 {
460  return (__cpp_object != src.__cpp_object);
461 }
462 
463 template <class T_CppObject> inline
465 {
466  return (__cpp_object != 0);
467 }
468 
469 template <class T_CppObject> inline
471 {
472  LockPtr<T_CppObject> temp; // swap with an empty LockPtr<> to clear *this
473  this->swap(temp);
474 }
475 
476 template <class T_CppObject>
477  template <class T_CastFrom>
478 inline
481 {
482  T_CppObject *const cpp_object = dynamic_cast<T_CppObject*>(src.operator->());
483 
484  if(cpp_object) //Check whether dynamic_cast<> succeeded so we don't pass a null object with a used refcount:
485  return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr());
486  else
487  return LockPtr<T_CppObject>();
488 }
489 
490 template <class T_CppObject>
491  template <class T_CastFrom>
492 inline
495 {
496  T_CppObject *const cpp_object = static_cast<T_CppObject*>(src.operator->());
497 
498  return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr());
499 }
500 
501 template <class T_CppObject>
502  template <class T_CastFrom>
503 inline
506 {
507  T_CppObject *const cpp_object = const_cast<T_CppObject*>(src.operator->());
508 
509  return LockPtr<T_CppObject>(cpp_object, src.refcount_ptr(), src.refmutex_ptr());
510 }
511 
512 
513 /** Swap refptr instances.
514  * @param lrp "left" refptr
515  * @param rrp "right" refptr
516  * @relates fawkes::LockPtr
517  */
518 template <class T_CppObject> inline
519 void
521 {
522  lrp.swap(rrp);
523 }
524 
525 } // end namespace fawkes
526 
527 #endif
bool operator==(const LockPtr< T_CppObject > &src) const
Tests whether the LockPtr&lt;&gt; point to the same underlying instance.
Definition: lockptr.h:451
bool operator!=(const LockPtr< T_CppObject > &src) const
Tests whether the LockPtr&lt;&gt; do not point to the same underlying instance.
Definition: lockptr.h:458
void unlock()
Unlock the mutex.
Definition: mutex.cpp:135
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:240
void lock() const
Lock access to the encapsulated object.
Definition: lockptr.h:225
static LockPtr< T_CppObject > cast_const(const LockPtr< T_CastFrom > &src)
Cast to non-const.
Definition: lockptr.h:505
Mutex * refmutex_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:221
int refcount() const
Get current refcount.
Definition: lockptr.h:207
LockPtr< T_CppObject > & operator=(const LockPtr< T_CppObject > &src)
Copy from another LockPtr.
Definition: lockptr.h:397
LockPtr&lt;&gt; is a reference-counting shared lockable smartpointer.
Definition: lockptr.h:57
static LockPtr< T_CppObject > cast_dynamic(const LockPtr< T_CastFrom > &src)
Dynamic cast to derived class.
Definition: lockptr.h:480
static LockPtr< T_CppObject > cast_static(const LockPtr< T_CastFrom > &src)
Static cast to derived class.
Definition: lockptr.h:494
bool try_lock() const
Try to acquire lock for the encapsulated object.
Definition: lockptr.h:230
~LockPtr()
Destructor - decrements reference count.
Definition: lockptr.h:276
void clear()
Set underlying instance to 0, decrementing reference count of existing instance appropriately.
Definition: lockptr.h:470
void swap(LockPtr< T_CppObject > &lrp, LockPtr< T_CppObject > &rrp)
Swap refptr instances.
Definition: lockptr.h:520
LockPtr()
Default constructor.
Definition: lockptr.h:266
void lock()
Lock this mutex.
Definition: mutex.cpp:89
Mutex mutual exclusion lock.
Definition: mutex.h:32
void unlock() const
Unlock object mutex.
Definition: lockptr.h:233
void swap(LockPtr< T_CppObject > &other)
Swap the contents of two LockPtr&lt;&gt;.
Definition: lockptr.h:377
int * refcount_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:215