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