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 *LockPtr<T_CppObject>::operator->() const
301 {
302  return cpp_object_;
303 }
304 
305 template <class T_CppObject>
306 inline T_CppObject *LockPtr<T_CppObject>::operator*() const
307 {
308  return cpp_object_;
309 }
310 
311 template <class T_CppObject>
312 inline LockPtr<T_CppObject>::LockPtr() : cpp_object_(0), obj_mutex_(0), ref_count_(0), ref_mutex_(0)
313 {
314 }
315 
316 template <class T_CppObject>
318 {
319  if (ref_count_ && ref_mutex_) {
320  ref_mutex_->lock();
321 
322  --(*ref_count_);
323 
324  if (*ref_count_ == 0) {
325  if (cpp_object_) {
326  delete cpp_object_;
327  cpp_object_ = 0;
328  }
329 
330  delete ref_count_;
331  delete ref_mutex_;
332  delete obj_mutex_;
333  ref_count_ = 0;
334  ref_mutex_ = 0;
335  } else {
336  ref_mutex_->unlock();
337  }
338  }
339 }
340 
341 template <class T_CppObject>
342 inline LockPtr<T_CppObject>::LockPtr(T_CppObject *cpp_object, bool recursive_mutex)
343 : cpp_object_(cpp_object), obj_mutex_(0), ref_count_(0), ref_mutex_(0)
344 {
345  if (cpp_object) {
346  ref_count_ = new int;
347  ref_mutex_ = new Mutex(Mutex::RECURSIVE);
348  obj_mutex_ = new Mutex(recursive_mutex ? Mutex::RECURSIVE : Mutex::NORMAL);
349  *ref_count_ = 1; //This will be decremented in the destructor.
350  }
351 }
352 
353 //Used by cast_*() implementations:
354 template <class T_CppObject>
355 inline LockPtr<T_CppObject>::LockPtr(T_CppObject *cpp_object,
356  Mutex * objmutex,
357  int * refcount,
358  Mutex * refmutex)
359 : cpp_object_(cpp_object), obj_mutex_(objmutex), ref_count_(refcount), ref_mutex_(refmutex)
360 {
361  if (cpp_object_ && obj_mutex_ && ref_count_ && ref_mutex_) {
362  ref_mutex_->lock();
363  ++(*ref_count_);
364  ref_mutex_->unlock();
365  }
366 }
367 
368 template <class T_CppObject>
370 : cpp_object_(src.cpp_object_),
371  obj_mutex_(src.obj_mutex_),
372  ref_count_(src.ref_count_),
373  ref_mutex_(src.ref_mutex_)
374 {
375  if (cpp_object_ && obj_mutex_ && ref_count_ && ref_mutex_) {
376  ref_mutex_->lock();
377  ++(*ref_count_);
378  ref_mutex_->unlock();
379  }
380 }
381 
382 // The templated ctor allows copy construction from any object that's
383 // castable. Thus, it does downcasts:
384 // base_ref = derived_ref
385 template <class T_CppObject>
386 template <class T_CastFrom>
388 : // A different LockPtr<> will not allow us access to cpp_object_. We need
389  // to add a get_underlying() for this, but that would encourage incorrect
390  // use, so we use the less well-known operator->() accessor:
391  cpp_object_(src.operator->()),
392  obj_mutex_(src.objmutex_ptr()),
393  ref_count_(src.refcount_ptr()),
394  ref_mutex_(src.refmutex_ptr())
395 {
396  if (cpp_object_ && obj_mutex_ && ref_count_ && ref_mutex_) {
397  ref_mutex_->lock();
398  ++(*ref_count_);
399  ref_mutex_->unlock();
400  }
401 }
402 
403 template <class T_CppObject>
404 inline void
406 {
407  T_CppObject *const temp = cpp_object_;
408  int * temp_count = ref_count_;
409  Mutex * temp_ref_mutex = ref_mutex_;
410  Mutex * temp_obj_mutex = obj_mutex_;
411 
412  cpp_object_ = other.cpp_object_;
413  obj_mutex_ = other.obj_mutex_;
414  ref_count_ = other.ref_count_;
415  ref_mutex_ = other.ref_mutex_;
416 
417  other.cpp_object_ = temp;
418  other.ref_count_ = temp_count;
419  other.ref_mutex_ = temp_ref_mutex;
420  other.obj_mutex_ = temp_obj_mutex;
421 }
422 
423 template <class T_CppObject>
424 inline LockPtr<T_CppObject> &
426 {
427  // In case you haven't seen the swap() technique to implement copy
428  // assignment before, here's what it does:
429  //
430  // 1) Create a temporary LockPtr<> instance via the copy ctor, thereby
431  // increasing the reference count of the source object.
432  //
433  // 2) Swap the internal object pointers of *this and the temporary
434  // LockPtr<>. After this step, *this already contains the new pointer,
435  // and the old pointer is now managed by temp.
436  //
437  // 3) The destructor of temp is executed, thereby unreferencing the
438  // old object pointer.
439  //
440  // This technique is described in Herb Sutter's "Exceptional C++", and
441  // has a number of advantages over conventional approaches:
442  //
443  // - Code reuse by calling the copy ctor.
444  // - Strong exception safety for free.
445  // - Self assignment is handled implicitely.
446  // - Simplicity.
447  // - It just works and is hard to get wrong; i.e. you can use it without
448  // even thinking about it to implement copy assignment whereever the
449  // object data is managed indirectly via a pointer, which is very common.
450 
451  LockPtr<T_CppObject> temp(src);
452  this->swap(temp);
453  return *this;
454 }
455 
456 template <class T_CppObject>
457 inline LockPtr<T_CppObject> &
459 {
460  LockPtr<T_CppObject> temp(ptr);
461  this->swap(temp);
462  return *this;
463 }
464 
465 template <class T_CppObject>
466 template <class T_CastFrom>
467 inline LockPtr<T_CppObject> &
469 {
470  LockPtr<T_CppObject> temp(src);
471  this->swap(temp);
472  return *this;
473 }
474 
475 template <class T_CppObject>
476 inline bool
478 {
479  return (cpp_object_ == src.cpp_object_);
480 }
481 
482 template <class T_CppObject>
483 inline bool
485 {
486  return (cpp_object_ != src.cpp_object_);
487 }
488 
489 template <class T_CppObject>
491 {
492  return (cpp_object_ != 0);
493 }
494 
495 template <class T_CppObject>
496 inline void
498 {
499  LockPtr<T_CppObject> temp; // swap with an empty LockPtr<> to clear *this
500  this->swap(temp);
501 }
502 
503 /** Swap refptr instances.
504  * @param lrp "left" refptr
505  * @param rrp "right" refptr
506  * @relates fawkes::LockPtr
507  */
508 template <class T_CppObject>
509 inline void
511 {
512  lrp.swap(rrp);
513 }
514 
515 } // end namespace fawkes
516 
517 #endif
bool operator!=(const LockPtr< T_CppObject > &src) const
Tests whether the LockPtr<> do not point to the same underlying instance.
Definition: lockptr.h:484
int * refcount_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:240
Mutex * refmutex_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:250
Fawkes library namespace.
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
T_CppObject * operator*() const
Get underlying pointer.
Definition: lockptr.h:306
void unlock() const
Unlock object mutex.
Definition: lockptr.h:273
T_CppObject * operator->() const
Dereferencing.
Definition: lockptr.h:300
LockPtr< T_CppObject > & operator=(const LockPtr< T_CppObject > &src)
Copy from another LockPtr.
Definition: lockptr.h:425
LockPtr<> is a reference-counting shared lockable smartpointer.
Definition: lockptr.h:54
bool try_lock() const
Try to acquire lock for the encapsulated object.
Definition: lockptr.h:266
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
bool operator==(const LockPtr< T_CppObject > &src) const
Tests whether the LockPtr<> point to the same underlying instance.
Definition: lockptr.h:477
~LockPtr()
Destructor - decrements reference count.
Definition: lockptr.h:317
static LockPtr< T_CppObject > cast_dynamic(const LockPtr< T_CastFrom > &src)
Dynamic cast to derived class.
Definition: lockptr.h:168
bool try_lock()
Tries to lock the mutex.
Definition: mutex.cpp:117
void clear()
Set underlying instance to 0, decrementing reference count of existing instance appropriately.
Definition: lockptr.h:497
void swap(LockPtr< T_CppObject > &lrp, LockPtr< T_CppObject > &rrp)
Swap refptr instances.
Definition: lockptr.h:510
static LockPtr< T_CppObject > cast_const(const LockPtr< T_CastFrom > &src)
Cast to non-const.
Definition: lockptr.h:208
LockPtr()
Default constructor.
Definition: lockptr.h:312
void lock()
Lock this mutex.
Definition: mutex.cpp:87
Mutex mutual exclusion lock.
Definition: mutex.h:32
void swap(LockPtr< T_CppObject > &other)
Swap the contents of two LockPtr<>.
Definition: lockptr.h:405
static LockPtr< T_CppObject > cast_static(const LockPtr< T_CastFrom > &src)
Static cast to derived class.
Definition: lockptr.h:190
void lock() const
Lock access to the encapsulated object.
Definition: lockptr.h:257
int refcount() const
Get current refcount.
Definition: lockptr.h:228