r3855 - in trunk/birnet: . tests



Author: timj
Date: 2006-08-14 19:23:06 -0400 (Mon, 14 Aug 2006)
New Revision: 3855

Modified:
   trunk/birnet/ChangeLog
   trunk/birnet/birnetsignal.hh
   trunk/birnet/birnetsignalslot.hh
   trunk/birnet/birnetthread.c
   trunk/birnet/birnetthread.h
   trunk/birnet/birnetthreadxx.cc
   trunk/birnet/birnetutilsxx.cc
   trunk/birnet/birnetutilsxx.hh
   trunk/birnet/tests/signal.cc
   trunk/birnet/tests/threads.cc
Log:
Tue Jul 18 01:14:22 2006  Tim Janik  <timj gtk org>

        * birnetthreadxx.cc: use birnet_*__chain4init() and 
        birnet_*__unchain() functions to construct initializer lists for
        mutexes and conditions. that way we don't require std::list to be
        constructed at static mutex construction time (which can be a
        problem).

        * birnetthread.h, birnetthread.c: provide chain4init and
        unchain variants for recursive mutexes and conditions. this required
        reordering of the BirnetRecMutex fields. provide static declaration
        macros for recursive mutexes and conditions in C.

        * birnetsignalslot.hh: hook trampolines into the destruction phase
        of a Deletable, if the instance of a method trampoline is a Deletable.
        as a side effect of this, slot-= only works for Deletable instance
        methods, *before* the instance is deleted.

        * birnetsignal.hh: provide VoidSlot and BoolSlot for convenience.

        * birnetutilsxx.hh, birnetutilsxx.cc: implemented class
        Deletable::DestructionHook which allowes hooking up (thread safe) of
        callbacks into the destruction phase of a Deletable.

        * tests/signal.cc: added tests for signal connections on temporary
        objects that trigger Deletable destruction hooks.

        * tests/threads.cc: test statically declared recursive mutextes and
        conditions in C.




Modified: trunk/birnet/ChangeLog
===================================================================
--- trunk/birnet/ChangeLog	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/ChangeLog	2006-08-14 23:23:06 UTC (rev 3855)
@@ -1,3 +1,33 @@
+Tue Jul 18 01:14:22 2006  Tim Janik  <timj gtk org>
+
+	* birnetthreadxx.cc: use birnet_*__chain4init() and 
+	birnet_*__unchain() functions to construct initializer lists for
+	mutexes and conditions. that way we don't require std::list to be
+	constructed at static mutex construction time (which can be a
+	problem).
+
+	* birnetthread.h, birnetthread.c: provide chain4init and
+	unchain variants for recursive mutexes and conditions. this required
+	reordering of the BirnetRecMutex fields. provide static declaration
+	macros for recursive mutexes and conditions in C.
+
+	* birnetsignalslot.hh: hook trampolines into the destruction phase
+	of a Deletable, if the instance of a method trampoline is a Deletable.
+	as a side effect of this, slot-= only works for Deletable instance
+	methods, *before* the instance is deleted.
+
+	* birnetsignal.hh: provide VoidSlot and BoolSlot for convenience.
+
+	* birnetutilsxx.hh, birnetutilsxx.cc: implemented class
+	Deletable::DestructionHook which allowes hooking up (thread safe) of
+	callbacks into the destruction phase of a Deletable.
+
+	* tests/signal.cc: added tests for signal connections on temporary
+	objects that trigger Deletable destruction hooks.
+
+	* tests/threads.cc: test statically declared recursive mutextes and
+	conditions in C.
+
 Fri Jul  7 02:15:50 2006  Tim Janik  <timj gtk org>
 
 	* tests/infotest.cc: implement file tests in terms of the C++ API

Modified: trunk/birnet/birnetsignal.hh
===================================================================
--- trunk/birnet/birnetsignal.hh	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetsignal.hh	2006-08-14 23:23:06 UTC (rev 3855)
@@ -42,7 +42,7 @@
   explicit        TrampolineLink() :
     next (NULL), prev (NULL), callable (true), with_emitter (false)
   {}
-  virtual bool operator== (const TrampolineLink &other) const = 0;
+  virtual bool    operator== (const TrampolineLink &other) const = 0;
   virtual         ~TrampolineLink()
   {
     if (next || prev)
@@ -439,6 +439,10 @@
 /* --- Trampoline + Slot + Signal generation --- */
 #include <birnet/birnetsignalvariants.hh> // contains multiple versions of "birnetsignaltemplate.hh"
 
+/* --- predefined slots --- */
+typedef Slot0<void, void> VoidSlot;
+typedef Slot0<bool, void> BoolSlot;
+
 /* --- predefined signals --- */
 template<class Emitter>
 struct SignalFinalize : Signal0 <Emitter, void, ScopeReferenceFinalizationMark> {
@@ -464,6 +468,8 @@
 using Signals::CollectorUntil0;
 using Signals::CollectorLast;
 using Signals::CollectorSum;
+using Signals::VoidSlot;
+using Signals::BoolSlot;
 using Signals::SignalFinalize;
 using Signals::SignalVoid;
 using Signals::Signal;

Modified: trunk/birnet/birnetsignalslot.hh
===================================================================
--- trunk/birnet/birnetsignalslot.hh	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetsignalslot.hh	2006-08-14 23:23:06 UTC (rev 3855)
@@ -38,7 +38,7 @@
   Callback callback;
   virtual R0 operator() (A1 a1, A2 a2, A3 a3)
   { return callback (a1, a2, a3); }
-  ~FunctionTrampoline3() {}
+  virtual      ~FunctionTrampoline3() {}
   virtual bool operator== (const TrampolineLink &bother) const {
     const FunctionTrampoline3 *other = dynamic_cast<const FunctionTrampoline3*> (&bother);
     return other and other->callback == callback; }
@@ -48,21 +48,21 @@
   {}
 };
 template<class Class, typename R0, typename A1, typename A2, typename A3>
-class MethodTrampoline3 : public Trampoline3 <R0, A1, A2, A3> {
+class MethodTrampoline3 : public Trampoline3 <R0, A1, A2, A3>, public virtual Deletable::DestructionHook {
   friend void FIXME_dummy_friend_for_gcc33();
   typedef R0 (Class::*Method) (A1, A2, A3);
   Class *instance;
   Method method;
   virtual R0 operator() (A1 a1, A2 a2, A3 a3)
   { return (instance->*method) (a1, a2, a3); }
-  ~MethodTrampoline3() {}
   virtual bool operator== (const TrampolineLink &bother) const {
     const MethodTrampoline3 *other = dynamic_cast<const MethodTrampoline3*> (&bother);
     return other and other->instance == instance and other->method == method; }
+  virtual      ~MethodTrampoline3()                     { deletable_remove_hook (instance); }
+  virtual void deletable_dispose (Deletable &deletable) { instance = NULL; this->callable = false; }
 public:
   MethodTrampoline3 (Class &obj, Method m) :
-    instance (&obj), method (m)
-  {}
+    instance (&obj), method (m)                         { deletable_add_hook (instance); }
 };
 
 /* --- Trampoline with Data --- */
@@ -73,7 +73,7 @@
   Callback callback; Data data;
   virtual R0 operator() (A1 a1, A2 a2, A3 a3)
   { return callback (a1, a2, a3, data); }
-  ~DataFunctionTrampoline3() {}
+  virtual      ~DataFunctionTrampoline3() {}
   virtual bool operator== (const TrampolineLink &bother) const {
     const DataFunctionTrampoline3 *other = dynamic_cast<const DataFunctionTrampoline3*> (&bother);
     return other and other->callback == callback and other->data == data; }
@@ -83,20 +83,20 @@
   {}
 };
 template<class Class, typename R0, typename A1, typename A2, typename A3, typename Data>
-class DataMethodTrampoline3 : public Trampoline3 <R0, A1, A2, A3> {
+class DataMethodTrampoline3 : public Trampoline3 <R0, A1, A2, A3>, public virtual Deletable::DestructionHook {
   friend void FIXME_dummy_friend_for_gcc33();
   typedef R0 (Class::*Method) (A1, A2, A3, Data);
   Class *instance; Method method; Data data;
   virtual R0 operator() (A1 a1, A2 a2, A3 a3)
   { return (instance->*method) (a1, a2, a3, data); }
-  ~DataMethodTrampoline3() {}
   virtual bool operator== (const TrampolineLink &bother) const {
     const DataMethodTrampoline3 *other = dynamic_cast<const DataMethodTrampoline3*> (&bother);
     return other and other->instance == instance and other->method == method and other->data == data; }
+  virtual      ~DataMethodTrampoline3()                 { deletable_remove_hook (instance); }
+  virtual void deletable_dispose (Deletable &deletable) { instance = NULL; this->callable = false; }
 public:
   DataMethodTrampoline3 (Class &obj, Method m, const Data &d) :
-    instance (&obj), method (m), data (d)
-  {}
+    instance (&obj), method (m), data (d)               { deletable_add_hook (instance); }
 };
 
 /* --- Slots (Trampoline wrappers) --- */

Modified: trunk/birnet/birnetthread.c
===================================================================
--- trunk/birnet/birnetthread.c	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetthread.c	2006-08-14 23:23:06 UTC (rev 3855)
@@ -1284,8 +1284,8 @@
 fallback_rec_mutex_init (BirnetRecMutex *rec_mutex)
 {
   rec_mutex->owner = NULL;
+  rec_mutex->depth = 0;
   birnet_mutex_init (&rec_mutex->mutex);
-  rec_mutex->depth = 0;
 }
 
 static int
@@ -1471,6 +1471,7 @@
 static void
 pth_rec_mutex_init (BirnetRecMutex *mutex)
 {
+  BIRNET_STATIC_ASSERT (offsetof (BirnetRecMutex, mutex) == 0);
   pthread_mutexattr_t attr;
   pthread_mutexattr_init (&attr);
   pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
@@ -1542,6 +1543,73 @@
 }
 
 void
+birnet_mutex__unchain (BirnetMutex *mutex)
+{
+  BirnetMutex *last = NULL, *m = mutex_init_chain;
+  while (m != mutex)
+    {
+      last = m;
+      m = last->mutex_pointer;
+    }
+  if (last)
+    last->mutex_pointer = mutex->mutex_pointer;
+  else
+    mutex_init_chain = mutex->mutex_pointer;
+}
+
+static BirnetMutex *rec_mutex_init_chain = NULL;
+
+void
+birnet_rec_mutex__chain4init (BirnetRecMutex *rec_mutex)
+{
+  BIRNET_STATIC_ASSERT (offsetof (BirnetRecMutex, mutex) == 0);
+  g_assert (rec_mutex->mutex.mutex_pointer == NULL);
+  rec_mutex->mutex.mutex_pointer = rec_mutex_init_chain;
+  rec_mutex_init_chain = &rec_mutex->mutex;
+}
+
+void
+birnet_rec_mutex__unchain (BirnetRecMutex *rec_mutex)
+{
+  BirnetMutex *mutex = (BirnetMutex*) rec_mutex;
+  BirnetMutex *last = NULL, *m = rec_mutex_init_chain;
+  while (m != mutex)
+    {
+      last = m;
+      m = last->mutex_pointer;
+    }
+  if (last)
+    last->mutex_pointer = mutex->mutex_pointer;
+  else
+    rec_mutex_init_chain = mutex->mutex_pointer;
+}
+
+static BirnetCond  *cond_init_chain = NULL;
+
+void
+birnet_cond__chain4init (BirnetCond *cond)
+{
+  g_assert (cond->cond_pointer == NULL);
+  cond->cond_pointer = cond_init_chain;
+  cond_init_chain = cond;
+}
+
+void
+birnet_cond__unchain (BirnetCond *cond)
+{
+  BirnetCond *last = NULL, *c = cond_init_chain;
+  while (c != cond)
+    {
+      last = c;
+      c = last->cond_pointer;
+    }
+  if (last)
+    last->cond_pointer = cond->cond_pointer;
+  else
+    cond_init_chain = cond->cond_pointer;
+}
+
+void
 _birnet_init_threads (void)
 {
   BirnetThreadTable *table = get_pth_thread_table ();
@@ -1561,6 +1629,19 @@
       mutex_init_chain = mutex->mutex_pointer;
       birnet_thread_table.mutex_init (mutex);
     }
+  while (rec_mutex_init_chain)
+    {
+      BirnetMutex *mutex = rec_mutex_init_chain;
+      rec_mutex_init_chain = mutex->mutex_pointer;
+      BIRNET_STATIC_ASSERT (offsetof (BirnetRecMutex, mutex) == 0);
+      birnet_thread_table.rec_mutex_init ((BirnetRecMutex*) mutex);
+    }
+  while (cond_init_chain)
+    {
+      BirnetCond *cond = cond_init_chain;
+      cond_init_chain = cond->cond_pointer;
+      birnet_thread_table.cond_init (cond);
+    }
 
   _birnet_init_threads_cxx();
 }

Modified: trunk/birnet/birnetthread.h
===================================================================
--- trunk/birnet/birnetthread.h	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetthread.h	2006-08-14 23:23:06 UTC (rev 3855)
@@ -137,6 +137,8 @@
 						     BirnetInt64    max_useconds);
 #define birnet_thread_exit(retval)		    (birnet_thread_table.thread_exit (retval))
 #define BIRNET_MUTEX_DECLARE_INITIALIZED(mutexname) BIRNET_MUTEX__DECLARE_INITIALIZED (mutexname)
+#define BIRNET_REC_MUTEX_DECLARE_INITIALIZED(rmnam) BIRNET_REC_MUTEX__DECLARE_INITIALIZED (rmnam)
+#define BIRNET_COND_DECLARE_INITIALIZED(condname)   BIRNET_COND__DECLARE_INITIALIZED (condname)
 
 /* --- atomic operations --- */
 extern inline void  birnet_atomic_int_set                  (volatile int      *atomic,
@@ -182,20 +184,35 @@
 #endif
 
 /* --- implementation --- */
-void  _birnet_init_threads      (void);
-void  _birnet_init_threads_cxx  (void);
-void* _birnet_thread_self_cxx   (void);
-void* _birnet_thread_get_cxx	(BirnetThread *thread);
-bool  _birnet_thread_set_cxx	(BirnetThread *thread,
-				 void         *xxdata);
-void  _birnet_thread_cxx_wrap	(BirnetThread *thread); /* in birnetthreadxx.cc */
-void  _birnet_thread_cxx_delete	(void         *thread); /* in birnetthreadxx.cc */
-void  birnet_mutex__chain4init  (BirnetMutex *mutex);
+void  _birnet_init_threads         (void);
+void  _birnet_init_threads_cxx     (void);
+void* _birnet_thread_self_cxx      (void);
+void* _birnet_thread_get_cxx	   (BirnetThread *thread);
+bool  _birnet_thread_set_cxx	   (BirnetThread *thread,
+				    void         *xxdata);
+void  _birnet_thread_cxx_wrap	   (BirnetThread *thread); /* in birnetthreadxx.cc */
+void  _birnet_thread_cxx_delete	   (void         *thread); /* in birnetthreadxx.cc */
+void  birnet_mutex__chain4init     (BirnetMutex *mutex);
+void  birnet_mutex__unchain        (BirnetMutex *mutex);
+void  birnet_rec_mutex__chain4init (BirnetRecMutex *rec_mutex);
+void  birnet_rec_mutex__unchain    (BirnetRecMutex *rec_mutex);
+void  birnet_cond__chain4init      (BirnetCond *cond);
+void  birnet_cond__unchain         (BirnetCond *cond);
 #define BIRNET_MUTEX__DECLARE_INITIALIZED(mutexname)                            \
   BirnetMutex mutexname = { 0 };                                                \
   static void __attribute__ ((constructor))                                     \
   BIRNET_CPP_PASTE4 (__birnet_mutex__autoinit, __LINE__, __, mutexname) (void)	\
   { birnet_mutex__chain4init (&mutexname); }
+#define BIRNET_REC_MUTEX__DECLARE_INITIALIZED(recmtx)                           \
+  BirnetRecMutex recmtx = { { 0 } };                                            \
+  static void __attribute__ ((constructor))                                     \
+  BIRNET_CPP_PASTE4 (__birnet_rec_mutex__autoinit, __LINE__, __, recmtx) (void)	\
+  { birnet_rec_mutex__chain4init (&recmtx); }
+#define BIRNET_COND__DECLARE_INITIALIZED(condname)                              \
+  BirnetCond condname = { 0 };                                                  \
+  static void __attribute__ ((constructor))                                     \
+  BIRNET_CPP_PASTE4 (__birnet_cond__autoinit, __LINE__, __, condname) (void)	\
+  { birnet_cond__chain4init (&condname); }
 
 union _BirnetCond
 {
@@ -209,9 +226,9 @@
 };
 struct _BirnetRecMutex
 {
+  BirnetMutex   mutex;
   BirnetThread *owner;
-  BirnetMutex   mutex;
-  guint      depth;
+  guint         depth;
 };
 struct _BirnetThreadTable
 {

Modified: trunk/birnet/birnetthreadxx.cc
===================================================================
--- trunk/birnet/birnetthreadxx.cc	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetthreadxx.cc	2006-08-14 23:23:06 UTC (rev 3855)
@@ -242,14 +242,12 @@
   birnet_thread_exit (retval);
 }
 
-static std::list<BirnetMutex*> cxx_init_mutex_list;
-
 Mutex::Mutex ()
 {
   if (birnet_threads_initialized())
     birnet_thread_table.mutex_init (&mutex);
   else
-    cxx_init_mutex_list.push_back (&mutex);
+    birnet_mutex__chain4init (&mutex);
 }
 
 Mutex::~Mutex ()
@@ -257,17 +255,15 @@
   if (birnet_threads_initialized())
     birnet_thread_table.mutex_destroy (&mutex);
   else
-    cxx_init_mutex_list.remove (&mutex);
+    birnet_mutex__unchain (&mutex);
 }
 
-static std::list<BirnetRecMutex*> cxx_init_rec_mutex_list;
-
 RecMutex::RecMutex ()
 {
   if (birnet_threads_initialized())
     birnet_thread_table.rec_mutex_init (&rmutex);
   else
-    cxx_init_rec_mutex_list.push_back (&rmutex);
+    birnet_rec_mutex__chain4init (&rmutex);
 }
 
 RecMutex::~RecMutex ()
@@ -275,17 +271,15 @@
   if (birnet_threads_initialized())
     birnet_thread_table.rec_mutex_destroy (&rmutex);
   else
-    cxx_init_rec_mutex_list.remove (&rmutex);
+    birnet_rec_mutex__unchain (&rmutex);
 }
 
-static std::list<BirnetCond*> cxx_init_cond_list;
-
 Cond::Cond ()
 {
   if (birnet_threads_initialized())
     birnet_thread_table.cond_init (&cond);
   else
-    cxx_init_cond_list.push_back (&cond);
+    birnet_cond__chain4init (&cond);
 }
 
 Cond::~Cond ()
@@ -293,7 +287,7 @@
   if (birnet_threads_initialized())
     birnet_thread_table.cond_destroy (&cond);
   else
-    cxx_init_cond_list.remove (&cond);
+    birnet_cond__unchain (&cond);
 }
 
 OwnedMutex::OwnedMutex () :
@@ -302,7 +296,7 @@
   if (birnet_threads_initialized())
     birnet_thread_table.rec_mutex_init (&m_rec_mutex);
   else
-    cxx_init_rec_mutex_list.push_back (&m_rec_mutex);
+    birnet_rec_mutex__chain4init (&m_rec_mutex);
 }
 
 OwnedMutex::~OwnedMutex()
@@ -311,7 +305,7 @@
   if (birnet_threads_initialized())
     birnet_thread_table.rec_mutex_destroy (&m_rec_mutex);
   else
-    cxx_init_rec_mutex_list.remove (&m_rec_mutex);
+    birnet_rec_mutex__unchain (&m_rec_mutex);
 }
 
 } // Birnet
@@ -320,24 +314,6 @@
 _birnet_init_threads_cxx (void)
 {
   using namespace Birnet;
-  while (!cxx_init_mutex_list.empty())
-    {
-      BirnetMutex *mutex = cxx_init_mutex_list.front();
-      cxx_init_mutex_list.pop_front();
-      birnet_thread_table.mutex_init (mutex);
-    }
-  while (!cxx_init_rec_mutex_list.empty())
-    {
-      BirnetRecMutex *rmutex = cxx_init_rec_mutex_list.front();
-      cxx_init_rec_mutex_list.pop_front();
-      birnet_thread_table.rec_mutex_init (rmutex);
-    }
-  while (!cxx_init_cond_list.empty())
-    {
-      BirnetCond *cond = cxx_init_cond_list.front();
-      cxx_init_cond_list.pop_front();
-      birnet_thread_table.cond_init (cond);
-    }
 }
 
 extern "C" void

Modified: trunk/birnet/birnetutilsxx.cc
===================================================================
--- trunk/birnet/birnetutilsxx.cc	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetutilsxx.cc	2006-08-14 23:23:06 UTC (rev 3855)
@@ -18,7 +18,7 @@
  */
 #include "birnetutilsxx.hh"
 #include "birnetutils.h"
-#include "birnetthread.h"
+#include "birnetthreadxx.hh"
 #include "birnetmsg.h"
 #include "birnetcpu.h"
 #include <sys/time.h>
@@ -220,6 +220,151 @@
 
 } // Path
 
+/* --- Deletable --- */
+
+/**
+ * @param deletable     possible Deletable* handle
+ * @return              TRUE if the hook was added
+ *
+ * Adds the destruction hook to @a deletable if it is non NULL.
+ * The destruction hook is asserted to be so far uninstalled.
+ * This function is MT-safe and may be called from any thread.
+ */
+bool
+Deletable::DestructionHook::deletable_add_hook (Deletable *deletable)
+{
+  if (deletable)
+    {
+      deletable->add_destruction_hook (this);
+      return true;
+    }
+  return false;
+}
+
+/**
+ * @param deletable     possible Deletable* handle
+ * @return              TRUE if the hook was removed
+ *
+ * Removes the destruction hook from @a deletable if it is non NULL.
+ * The destruction hook is asserted to be installed on @a deletable.
+ * This function is MT-safe and may be called from any thread.
+ */
+bool
+Deletable::DestructionHook::deletable_remove_hook (Deletable *deletable)
+{
+  if (deletable)
+    {
+      deletable->remove_destruction_hook (this);
+      return true;
+    }
+  return false;
+}
+
+static struct {
+  Mutex                                            mutex;
+  std::map<Deletable*,Deletable::DestructionHook*> dmap;
+} deletable_maps[19]; /* use prime size for hashing, sum up to roughly 1k (use 83 for 4k) */
+
+/**
+ * @param hook  valid destruction hook
+ *
+ * Add an uninstalled destruction hook to the deletable.
+ * This function is MT-safe and may be called from any thread.
+ */
+void
+Deletable::add_destruction_hook (DestructionHook *hook)
+{
+  uint32 hashv = ((gsize) (void*) this) % (sizeof (deletable_maps) / sizeof (deletable_maps[0]));
+  deletable_maps[hashv].mutex.lock();
+  BIRNET_ASSERT (hook);
+  BIRNET_ASSERT (!hook->next);
+  BIRNET_ASSERT (!hook->prev);
+  std::map<Deletable*,DestructionHook*>::iterator it;
+  it = deletable_maps[hashv].dmap.find (this);
+  if (it != deletable_maps[hashv].dmap.end())
+    {
+      hook->next = it->second;
+      it->second = hook;
+      if (hook->next)
+        hook->next->prev = hook;
+    }
+  else
+    deletable_maps[hashv].dmap[this] = hook;
+  deletable_maps[hashv].mutex.unlock();
+  //g_printerr ("DELETABLE-ADD(%p,%p)\n", this, hook);
+}
+
+/**
+ * @param hook  valid destruction hook
+ *
+ * Remove a previously added destruction hook.
+ * This function is MT-safe and may be called from any thread.
+ */
+void
+Deletable::remove_destruction_hook (DestructionHook *hook)
+{
+  uint32 hashv = ((gsize) (void*) this) % (sizeof (deletable_maps) / sizeof (deletable_maps[0]));
+  deletable_maps[hashv].mutex.lock();
+  BIRNET_ASSERT (hook);
+  if (hook->next)
+    hook->next->prev = hook->prev;
+  if (hook->prev)
+    hook->prev->next = hook->next;
+  else
+    {
+      std::map<Deletable*,DestructionHook*>::iterator it;
+      it = deletable_maps[hashv].dmap.find (this);
+      BIRNET_ASSERT (it != deletable_maps[hashv].dmap.end());
+      BIRNET_ASSERT (it->second == hook);
+      it->second = hook->next;
+    }
+  hook->prev = NULL;
+  hook->next = NULL;
+  deletable_maps[hashv].mutex.unlock();
+  //g_printerr ("DELETABLE-REM(%p,%p)\n", this, hook);
+}
+
+/**
+ * Invoke all destruction hooks installed on this deletable.
+ */
+void
+Deletable::invoke_destruction_hooks()
+{
+  uint32 hashv = ((gsize) (void*) this) % (sizeof (deletable_maps) / sizeof (deletable_maps[0]));
+  while (TRUE)
+    {
+      /* lookup hook list */
+      deletable_maps[hashv].mutex.lock();
+      std::map<Deletable*,DestructionHook*>::iterator it;
+      DestructionHook *hooks;
+      it = deletable_maps[hashv].dmap.find (this);
+      if (it != deletable_maps[hashv].dmap.end())
+        {
+          hooks = it->second;
+          deletable_maps[hashv].dmap.erase (it);
+        }
+      else
+        hooks = NULL;
+      deletable_maps[hashv].mutex.unlock();
+      /* we're done if all hooks have been procesed */
+      if (!hooks)
+        break;
+      /* process hooks */
+      while (hooks)
+        {
+          DestructionHook *hook = hooks;
+          hooks = hook->next;
+          if (hooks)
+            hooks->prev = NULL;
+          hook->prev = NULL;
+          hook->next = NULL;
+          //g_printerr ("DELETABLE-DIS(%p,%p)\n", this, hook);
+          hook->deletable_dispose (*this);
+        }
+    }
+}
+
+
 /* --- DataList --- */
 void
 DataList::set_data (NodeBase *node)

Modified: trunk/birnet/birnetutilsxx.hh
===================================================================
--- trunk/birnet/birnetutilsxx.hh	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/birnetutilsxx.hh	2006-08-14 23:23:06 UTC (rev 3855)
@@ -105,9 +105,25 @@
 }
 
 /* --- Deletable --- */
-class Deletable {
+struct Deletable {
+  class DestructionHook {
+    DestructionHook    *prev;
+    DestructionHook    *next;
+    friend class Deletable;
+  public:
+    explicit            DestructionHook       () : prev (NULL), next (NULL) {}
+    virtual void        deletable_dispose     (Deletable &deletable) = 0;
+    bool                deletable_add_hook    (void      *any)              { return false; }
+    bool                deletable_add_hook    (Deletable *deletable);
+    bool                deletable_remove_hook (void      *any)              { return false; }
+    bool                deletable_remove_hook (Deletable *deletable);
+  };
+private:
+  void    add_destruction_hook     (DestructionHook *hook);
+  void    remove_destruction_hook  (DestructionHook *hook);
 protected:
-  virtual ~Deletable() {}
+  void    invoke_destruction_hooks ();
+  virtual ~Deletable() { invoke_destruction_hooks(); }
 };
 
 /* --- ReferenceCountImpl --- */
@@ -328,7 +344,7 @@
   public:
     T   get_data ()     { return data; }
     T   swap     (T d)  { T result = data; data = d; return result; }
-    ~Node()
+    virtual ~Node()
     {
       if (key)
         {

Modified: trunk/birnet/tests/signal.cc
===================================================================
--- trunk/birnet/tests/signal.cc	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/tests/signal.cc	2006-08-14 23:23:06 UTC (rev 3855)
@@ -214,6 +214,83 @@
   }
 };
 
+static uint assertion_counter = 0;
+struct TemporaryObject : public virtual Deletable {
+  String msg, msg2;
+  TemporaryObject() :
+    msg ("TemporaryObject"), msg2 ("Blub")
+  {}
+  String string_callback (int i, String s, float f)
+  {
+    assertion_counter++;
+    TPRINT ("  callback: %s (%d, %s, %f); [%s]\n", __func__, i, s.c_str(), f, msg.c_str());
+    return __func__;
+  }
+  String string_emitter_callback (Emitter3 &emitter, int i, String s, float f)
+  {
+    assertion_counter++;
+    TPRINT ("  callback: %s (%d, %s, %f); [%s]\n", __func__, i, s.c_str(), f, msg.c_str());
+    return __func__;
+  }
+  void void_callback (int i, String s, float f)
+  {
+    assertion_counter++;
+    TPRINT ("  callback: %s (%d, %s, %f); [%s]\n", __func__, i, s.c_str(), f, msg2.c_str());
+  }
+  void void_emitter_callback (Emitter3 &emitter, int i, String s, float f)
+  {
+    assertion_counter++;
+    TPRINT ("  callback: %s (%d, %s, %f); [%s]\n", __func__, i, s.c_str(), f, msg2.c_str());
+  }
+  void never_ever_call_me (int i, String s, float f)
+  {
+    TASSERT (1 == 0);
+  }
+  static void
+  test_temporary_object (Emitter3 &e3)
+  {
+    TSTART ("Signals, temporary object");
+    uint ac = assertion_counter;
+    {
+      TemporaryObject tobj;
+      e3.sig_mixed += slot (tobj, &TemporaryObject::string_emitter_callback);
+      e3.sig_void_mixed += slot (tobj, &TemporaryObject::never_ever_call_me);
+      e3.sig_mixed += slot (tobj, &TemporaryObject::string_callback);
+      {
+        TemporaryObject tmp2;
+        e3.sig_void_mixed += slot (tmp2, &TemporaryObject::never_ever_call_me);
+        /* auto-disconnected at end of scope */
+      }
+      {
+        Emitter3 tmp_emitter;
+        tmp_emitter.sig_mixed += slot (tobj, &TemporaryObject::string_emitter_callback);
+        tmp_emitter.sig_mixed += slot (tobj, &TemporaryObject::string_callback);
+        tmp_emitter.sig_void_mixed += slot (tobj, &TemporaryObject::never_ever_call_me);
+        TemporaryObject tobj2;
+        tmp_emitter.sig_void_mixed += slot (tobj2, &TemporaryObject::never_ever_call_me);
+        tmp_emitter.sig_void_mixed += slot (tobj2, &TemporaryObject::never_ever_call_me);
+        tmp_emitter.sig_void_mixed -= slot (tobj2, &TemporaryObject::never_ever_call_me);
+        /* remove all slots from tmp_emitter */
+      }
+      e3.sig_void_mixed -= slot (tobj, &TemporaryObject::never_ever_call_me);
+      TASSERT (ac == assertion_counter);
+      e3.test_emissions();
+      TASSERT (ac < assertion_counter);
+      e3.sig_void_mixed += slot (tobj, &TemporaryObject::void_emitter_callback);
+      e3.sig_void_mixed += slot (tobj, &TemporaryObject::void_callback);
+      ac = assertion_counter;
+      e3.test_emissions();
+      TASSERT (ac < assertion_counter);
+      e3.sig_void_mixed += slot (tobj, &TemporaryObject::never_ever_call_me);
+      /* here, Deletable::invoke_destruction_hooks() is called */
+    }
+    ac = assertion_counter;
+    e3.test_emissions(); // all handlers got disconnected
+    TASSERT (ac == assertion_counter);
+    TDONE();
+  }
+};
+
 static int tst_counter = 0;
 static int
 increment_tst_counter (void)
@@ -259,10 +336,14 @@
   signal_test.basic_signal_tests();
   signal_test.member_pointer_tests();
 #endif
+  Emitter3 e3;
+  TemporaryObject::test_temporary_object (e3);
   Connection3 c3;
-  Emitter3 e3;
+  Connection3 c3b;
   c3.test_signal (e3);
-  EmitterMany many;
-  many.testme();
+  c3b.test_signal (e3);
+  EmitterMany many1, many2;
+  many1.testme();
+  many2.testme();
   return 0;
 }

Modified: trunk/birnet/tests/threads.cc
===================================================================
--- trunk/birnet/tests/threads.cc	2006-08-14 23:11:22 UTC (rev 3854)
+++ trunk/birnet/tests/threads.cc	2006-08-14 23:23:06 UTC (rev 3855)
@@ -96,6 +96,8 @@
 }
 
 static BIRNET_MUTEX_DECLARE_INITIALIZED (static_mutex);
+static BIRNET_REC_MUTEX_DECLARE_INITIALIZED (static_rec_mutex);
+static BIRNET_COND_DECLARE_INITIALIZED (static_cond);
 
 static void
 test_threads (void)
@@ -120,6 +122,19 @@
   locked = birnet_mutex_trylock (&static_mutex);
   TASSERT (locked);
   birnet_mutex_unlock (&static_mutex);
+  /* not initializing static_rec_mutex */
+  locked = birnet_rec_mutex_trylock (&static_rec_mutex);
+  TASSERT (locked);
+  locked = birnet_rec_mutex_trylock (&static_rec_mutex);
+  TASSERT (locked);
+  birnet_rec_mutex_unlock (&static_rec_mutex);
+  birnet_rec_mutex_unlock (&static_rec_mutex);
+  locked = birnet_rec_mutex_trylock (&static_rec_mutex);
+  TASSERT (locked);
+  birnet_rec_mutex_unlock (&static_rec_mutex);
+  /* not initializing static_cond */
+  birnet_cond_signal (&static_cond);
+  birnet_cond_broadcast (&static_cond);
   /* test C++ mutex */
   static Mutex mutex;
   static RecMutex rmutex;




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]