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
32namespace 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 */
53template <class T_CppObject>
55{
56public:
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 *
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 *
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 *
285 {
286 return obj_mutex_;
287 }
288
289private:
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
299template <class T_CppObject>
300inline T_CppObject *
302{
303 return cpp_object_;
304}
305
306template <class T_CppObject>
307inline T_CppObject *
309{
310 return cpp_object_;
311}
312
313template <class T_CppObject>
314inline LockPtr<T_CppObject>::LockPtr() : cpp_object_(0), obj_mutex_(0), ref_count_(0), ref_mutex_(0)
315{
316}
317
318template <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
343template <class T_CppObject>
344inline 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:
356template <class T_CppObject>
357inline 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
370template <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
387template <class T_CppObject>
388template <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
405template <class T_CppObject>
406inline 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
425template <class 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
458template <class T_CppObject>
461{
462 LockPtr<T_CppObject> temp(ptr);
463 this->swap(temp);
464 return *this;
465}
466
467template <class T_CppObject>
468template <class T_CastFrom>
471{
472 LockPtr<T_CppObject> temp(src);
473 this->swap(temp);
474 return *this;
475}
476
477template <class T_CppObject>
478inline bool
480{
481 return (cpp_object_ == src.cpp_object_);
482}
483
484template <class T_CppObject>
485inline bool
487{
488 return (cpp_object_ != src.cpp_object_);
489}
490
491template <class T_CppObject>
493{
494 return (cpp_object_ != 0);
495}
496
497template <class T_CppObject>
498inline 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 */
510template <class T_CppObject>
511inline 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
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
static LockPtr< T_CppObject > cast_static(const LockPtr< T_CastFrom > &src)
Static cast to derived class.
Definition: lockptr.h:190
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
int * refcount_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:240
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
static LockPtr< T_CppObject > cast_const(const LockPtr< T_CastFrom > &src)
Cast to non-const.
Definition: lockptr.h:208
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
T_CppObject * operator->() const
Dereferencing.
Definition: lockptr.h:301
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
void unlock() const
Unlock object mutex.
Definition: lockptr.h:273
static LockPtr< T_CppObject > cast_dynamic(const LockPtr< T_CastFrom > &src)
Dynamic cast to derived class.
Definition: lockptr.h:168
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
Mutex * refmutex_ptr() const
For use only in the internal implementation of sharedptr.
Definition: lockptr.h:250
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.