Fix potential destroying of an unitialized mutex in class Synchronized. (fix by Glenn...
[dbus-cxx-async.git] / include / dbus-c++ / util.h
blob3f6f30911225fd6c45c8268fc0010cccff8651ae
1 /*
3 * D-Bus++ - C++ bindings for D-Bus
5 * Copyright (C) 2005-2009 Paolo Durante <shackan@gmail.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifndef __DBUSXX_UTIL_H
26 #define __DBUSXX_UTIL_H
28 #include <pthread.h>
29 #include <set>
31 #include "api.h"
32 #include "debug.h"
34 #include <list>
36 namespace DBus {
39 * Very simple reference counting
42 class DBUSXXAPI RefCnt
44 public:
46 RefCnt()
48 __ref = new int;
49 (*__ref) = 1;
50 __mtx = new pthread_mutex_t;
51 pthread_mutex_init(__mtx, NULL);
54 RefCnt(const RefCnt &rc)
56 __ref = rc.__ref;
57 __mtx = rc.__mtx;
58 ref();
61 virtual ~RefCnt()
63 unref();
66 RefCnt &operator = (const RefCnt &ref)
68 if (&ref != this)
70 ref.ref();
71 unref();
72 __ref = ref.__ref;
73 __mtx = ref.__mtx;
75 return *this;
78 bool noref() const
80 return (*__ref) == 0;
83 bool one() const
85 return (*__ref) == 1;
88 void lock() const
90 if ( 0 != __mtx )
92 pthread_mutex_lock(__mtx);
96 void unlock() const
98 if ( 0 != __mtx )
100 pthread_mutex_unlock(__mtx);
104 private:
106 DBUSXXAPILOCAL void ref() const
108 ++ (*__ref);
110 DBUSXXAPILOCAL void unref()
112 -- (*__ref);
114 if ((*__ref) < 0)
116 debug_log("%p: refcount dropped below zero!", __ref);
119 if (noref())
121 delete __ref;
122 __ref = 0;
123 pthread_mutex_destroy(__mtx);
124 delete __mtx;
125 __mtx = 0;
129 private:
131 int *__ref;
132 pthread_mutex_t* __mtx;
136 * Pthreads-based mutual exclusion
138 class Synchronized
140 protected:
142 struct Guard
144 Guard(const Synchronized *s) : _s(s)
146 _s->lock();
148 ~Guard()
150 _s->unlock();
152 const Synchronized *_s;
155 void lock() const
157 pthread_mutex_lock(&__mtx);
160 void unlock() const
162 pthread_mutex_unlock(&__mtx);
165 protected:
167 Synchronized()
169 pthread_mutex_init(&__mtx, NULL);
172 public:
174 ~Synchronized()
176 pthread_mutex_destroy(&__mtx);
179 private:
181 // Not supported - unimplemented
182 Synchronized(const Synchronized& lhs);
183 Synchronized& operator=(const Synchronized& ref);
185 mutable pthread_mutex_t __mtx;
189 * Reference counting pointers (emulate boost::shared_ptr)
192 class RefPtrVoid
194 public:
196 RefPtrVoid(void *ptr)
197 : __ptr(ptr)
198 , __cnt()
201 RefPtrVoid(const RefPtrVoid& lhs)
202 : __ptr(0)
203 , __cnt()
206 virtual ~RefPtrVoid()
209 bool operator == (const RefPtrVoid &ref) const
211 return __ptr == ref.__ptr;
214 protected:
216 void *__ptr;
217 RefCnt __cnt;
220 template <class T>
221 class RefPtrI : public RefPtrVoid // RefPtr to incomplete type
223 public:
225 RefPtrI(T *ptr = 0);
227 RefPtrI(const RefPtrI& lhs);
229 ~RefPtrI();
231 RefPtrI &operator = (const RefPtrI &ref)
233 if (this != &ref)
235 __cnt.lock();
237 // If there is only a single reference (that's us) then ...
238 if (__cnt.one())
240 delete get();
241 __ptr = 0;
244 // If "ref" is referencing something other than what we're
245 // referencing
246 if ( __ptr != ref.__ptr )
248 ref.__cnt.lock();
251 __ptr = ref.__ptr;
252 __cnt = ref.__cnt;
254 // At this point __cnt == __ref.__cnt so unlocking __cnt
255 // effectively unlocks ref.__cnt as well.
256 __cnt.unlock();
258 return *this;
261 T &operator *() const
263 return *get();
266 T *operator ->() const
268 return get();
271 T *get() const
273 return static_cast<T *>(__ptr);
277 template <class T>
278 class RefPtr : public RefPtrVoid
280 public:
282 RefPtr(T *ptr = 0)
283 : RefPtrVoid(ptr)
286 RefPtr(const T& lhs)
287 : RefPtrVoid()
289 *this = lhs;
292 ~RefPtr()
294 __cnt.lock();
295 if (__cnt.one()) delete get();
296 __cnt.unlock();
299 RefPtr &operator = (const RefPtr &ref)
301 if (this != &ref)
303 __cnt.lock();
305 // If there is only a single reference (that's us) then ...
306 if (__cnt.one())
308 delete get();
309 __ptr = 0;
312 // If "ref" is referencing something other than what we're
313 // referencing
314 if ( __ptr != ref.__ptr )
316 ref.__cnt.lock();
319 __ptr = ref.__ptr;
320 __cnt = ref.__cnt;
322 // At this point __cnt == __ref.__cnt so unlocking __cnt
323 // effectively unlocks ref.__cnt as well.
324 __cnt.unlock();
326 return *this;
329 T &operator *() const
331 return *get();
334 T *operator ->() const
336 return get();
339 T *get() const
341 return static_cast<T *>(__ptr);
346 * Typed callback template
350 // Forward Declarations
352 class CallbackEmitter;
353 class CallbackTarget;
355 class CallbackEmitter : protected Synchronized
357 public:
358 CallbackEmitter();
359 virtual ~CallbackEmitter();
361 protected:
362 void connect(CallbackTarget* tgt);
364 void disconnect();
366 bool isConnected() const;
368 private:
369 CallbackTarget* target;
370 friend class CallbackTarget;
373 class CallbackTarget : protected Synchronized
375 public:
376 CallbackTarget();
377 virtual ~CallbackTarget();
379 private:
380 void disconnect_emitter(CallbackEmitter* cbEmitter);
381 void connect_emitter(CallbackEmitter* cbEmitter);
383 typedef std::set<CallbackEmitter*> EmitterCollection;
384 EmitterCollection emitters;
385 friend class CallbackEmitter;
389 // CallbackTarget Implementation
391 inline CallbackTarget::CallbackTarget()
392 : Synchronized()
393 , emitters()
395 debug_log("CallbackTarget(%p)::Constructed");
398 inline CallbackTarget::~CallbackTarget()
400 Guard g(this);
401 EmitterCollection::const_iterator it;
402 // Tell all the callback emitters that this target is
403 // being disconnected.
404 debug_log("CallbackTarget(%p)::# Emitters to disconnect: %u",
405 this, emitters.size());
407 for ( it = emitters.begin(); it != emitters.end(); ++it )
409 debug_log("CallbackTarget(%p)::Disconnect emitter: %p", this, *it);
410 (*it)->disconnect();
412 emitters.clear();
413 debug_log("CallbackTarget(%p)::Destroyed", this);
416 inline void CallbackTarget::disconnect_emitter(CallbackEmitter* cbEmitter)
418 Guard g(this);
419 debug_log("CallbackTarget(%p)::Erasing emitter: %p", this, cbEmitter);
420 emitters.erase(cbEmitter);
423 inline void CallbackTarget::connect_emitter(CallbackEmitter* cbEmitter)
425 Guard g(this);
426 debug_log("CallbackTarget(%p)::Inserting emitter: %p", this, cbEmitter);
427 emitters.insert(cbEmitter);
431 // CallbackEmitter Implementation
434 inline CallbackEmitter::CallbackEmitter()
435 : Synchronized()
436 , target(0)
438 debug_log("CallbackEmitter(%p)::Constructed", this);
441 inline CallbackEmitter::~CallbackEmitter()
443 Guard g(this);
445 if ( target )
447 debug_log("CallbackEmitter(%p)::Disconnecting from target: %p", this, target);
448 target->disconnect_emitter(this);
451 debug_log("CallbackEmitter(%p)::Destroyed", this);
454 inline void CallbackEmitter::connect(CallbackTarget* tgt)
456 Guard g(this);
458 if ( target && (target != tgt) )
460 // Unregister from the (old) target first
461 debug_log("CallbackEmitter(%p)::Unregistering emitter from target: %p", this, target);
462 target->disconnect_emitter(this);
465 target = tgt;
466 if ( target )
468 debug_log("CallbackEmitter(%p)::Connecting to target: %p", this, target);
469 target->connect_emitter(this);
473 inline void CallbackEmitter::disconnect()
475 Guard g(this);
476 debug_log("CallbackEmitter(%p)::Disconnecting target: %p", this, target);
477 target = 0;
480 inline bool CallbackEmitter::isConnected() const
482 return 0 != target;
486 template <class R, class P>
487 class Callback_Base : public CallbackEmitter
489 public:
490 Callback_Base() : CallbackEmitter() {}
492 virtual R call(P param) const = 0;
494 virtual ~Callback_Base()
498 template <class R, class P>
499 class Slot
501 public:
503 Slot &operator = (Callback_Base<R,P>* s)
505 _cb = s;
507 return *this;
510 R operator()(P param) const
512 /*if (_cb.get())*/ return _cb->call(param);
515 R call(P param) const
517 /*if (_cb.get())*/ return _cb->call(param);
520 bool empty()
522 return _cb.get() == 0;
525 private:
527 RefPtr< Callback_Base<R,P> > _cb;
531 template <class C, class P>
532 class CallbackNoReturn : public Callback_Base<void,P>
534 public:
536 typedef void (C::*M)(P);
538 CallbackNoReturn(C *c, M m)
539 : _c(c), _m(m)
541 debug_log("CallbackNoReturn(%p)::Ctor - connect to target: %p", this, c);
542 CallbackEmitter::connect(c);
545 virtual ~CallbackNoReturn()
547 debug_log("CallbackNoReturn(%p)::Destroyed", this);
550 void call(P param) const
552 Synchronized::Guard g(this);
554 if ( CallbackEmitter::isConnected() )
556 (_c->*_m)(param);
558 return;
561 private:
563 C *_c; M _m;
566 template <class C, class R, class P>
567 class Callback : public Callback_Base<R,P>
569 public:
571 typedef R (C::*M)(P);
573 Callback(C *c, M m, const R& def)
574 : _c(c), _m(m), _d(def)
576 debug_log("Callback(%p)::Ctor - connect to target: %p", this, c);
577 CallbackEmitter::connect(c);
580 virtual ~Callback()
582 debug_log("Callback(%p)::Destroyed", this);
585 R call(P param) const
587 Synchronized::Guard g(this);
589 if ( CallbackEmitter::isConnected() )
591 return (_c->*_m)(param);
593 return _d;
596 private:
598 C *_c; M _m; R _d;
601 } /* namespace DBus */
603 #endif//__DBUSXX_UTIL_H