2003-03-23 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Mon, 24 Mar 2003 03:16:58 +0000 (03:16 +0000)
committerHavoc Pennington <hp@redhat.com>
Mon, 24 Mar 2003 03:16:58 +0000 (03:16 +0000)
* dbus/dbus-threads.c (dbus_mutex_new, dbus_condvar_new): with
DBUS_BUILD_TESTS, actually alloc/free a block of memory for
the mutex, so we can check for proper memory management
and OOM handling.

* dbus/dbus-dataslot.c: remove the mutex from
DBusDataSlotAllocator and lock it manually when using it,
to simplify fitting it into the global slots framework.

* dbus/dbus-threads.c (init_static_locks): rework how we're
handling global locks so they are easily shut down.

* bus/policy.c (bus_policy_append_rule): fix

* bus/test-main.c (main): check for memleaks

* dbus/dbus-test.c (dbus_internal_do_not_use_run_tests): make
test suite check for memleaks

* dbus/dbus-memory.c: add support in test mode for tracking
number of outstanding blocks

18 files changed:
ChangeLog
bus/policy.c
bus/test-main.c
dbus/dbus-bus.c
dbus/dbus-connection.c
dbus/dbus-dataslot.c
dbus/dbus-dataslot.h
dbus/dbus-internals.c
dbus/dbus-internals.h
dbus/dbus-list.c
dbus/dbus-memory.c
dbus/dbus-memory.h
dbus/dbus-message-handler.c
dbus/dbus-server.c
dbus/dbus-sysdeps.c
dbus/dbus-test-main.c
dbus/dbus-test.c
dbus/dbus-threads.c

index aad1a71..3db6d96 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,29 @@
 2003-03-23  Havoc Pennington  <hp@pobox.com>
 
+       * dbus/dbus-threads.c (dbus_mutex_new, dbus_condvar_new): with 
+       DBUS_BUILD_TESTS, actually alloc/free a block of memory for 
+       the mutex, so we can check for proper memory management 
+       and OOM handling.
+
+       * dbus/dbus-dataslot.c: remove the mutex from
+       DBusDataSlotAllocator and lock it manually when using it, 
+       to simplify fitting it into the global slots framework.
+
+       * dbus/dbus-threads.c (init_static_locks): rework how we're
+       handling global locks so they are easily shut down.
+
+       * bus/policy.c (bus_policy_append_rule): fix
+
+       * bus/test-main.c (main): check for memleaks
+
+       * dbus/dbus-test.c (dbus_internal_do_not_use_run_tests): make 
+       test suite check for memleaks
+
+       * dbus/dbus-memory.c: add support in test mode for tracking 
+       number of outstanding blocks
+
+2003-03-23  Havoc Pennington  <hp@pobox.com>
+
        * bus/policy.c, bus/bus.c, bus/connection.c: implement allow/deny
        policies code
        
index 015757a..75013c8 100644 (file)
@@ -73,6 +73,10 @@ bus_policy_rule_unref (BusPolicyRule *rule)
         case BUS_POLICY_RULE_OWN:
           dbus_free (rule->d.own.service_name);
           break;
+        case BUS_POLICY_RULE_USER:
+        case BUS_POLICY_RULE_GROUP:
+          _dbus_assert_not_reached ("invalid rule");
+          break;
         }
       
       dbus_free (rule);
@@ -203,6 +207,10 @@ bus_policy_optimize (BusPolicy *policy)
           remove_preceding =
             rule->d.own.service_name == NULL;
           break;
+        case BUS_POLICY_RULE_USER:
+        case BUS_POLICY_RULE_GROUP:
+          _dbus_assert_not_reached ("invalid rule");
+          break;
         }
                 
       if (remove_preceding)
@@ -220,7 +228,7 @@ dbus_bool_t
 bus_policy_append_rule (BusPolicy     *policy,
                         BusPolicyRule *rule)
 {
-  if (!_dbus_list_append (policy->rules, rule))
+  if (!_dbus_list_append (&policy->rules, rule))
     return FALSE;
 
   bus_policy_rule_ref (rule);
index 278e4a8..2e0b952 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <dbus/dbus-string.h>
 #include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-internals.h>
 
 static void
 die (const char *failure)
@@ -55,6 +56,16 @@ main (int argc, char **argv)
   if (!bus_dispatch_test (&test_data_dir))
     die ("dispatch");
 
+  dbus_shutdown ();
+  
+  printf ("%s: checking for memleaks\n", argv[0]);
+  if (_dbus_get_malloc_blocks_outstanding () != 0)
+    {
+      _dbus_warn ("%d dbus_malloc blocks were not freed\n",
+                  _dbus_get_malloc_blocks_outstanding ());
+      die ("memleaks");
+    }
+  
   printf ("%s: Success\n", argv[0]);
   
   return 0;
index c05d74a..7a53938 100644 (file)
@@ -63,24 +63,12 @@ static int bus_data_slot_refcount = 0;
 /**
  * Lock for bus_data_slot and bus_data_slot_refcount
  */
-static DBusMutex *slot_lock;
-
-/**
- * Initialize the mutex used for bus_data_slot
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_bus_init_lock (void)
-{
-  slot_lock = dbus_mutex_new ();
-  return slot_lock;
-}
+_DBUS_DEFINE_GLOBAL_LOCK (bus);
 
 static dbus_bool_t
 data_slot_ref (void)
 {
-  dbus_mutex_lock (slot_lock);
+  _DBUS_LOCK (bus);
 
   if (bus_data_slot < 0)
     {
@@ -88,7 +76,7 @@ data_slot_ref (void)
       
       if (bus_data_slot < 0)
         {
-          dbus_mutex_unlock (slot_lock);
+          _DBUS_UNLOCK (bus);
           return FALSE;
         }
 
@@ -97,7 +85,7 @@ data_slot_ref (void)
 
   bus_data_slot_refcount += 1;
 
-  dbus_mutex_unlock (slot_lock);
+  _DBUS_UNLOCK (bus);
 
   return TRUE;
 }
@@ -105,7 +93,7 @@ data_slot_ref (void)
 static void
 data_slot_unref (void)
 {
-  dbus_mutex_lock (slot_lock);
+  _DBUS_LOCK (bus);
 
   _dbus_assert (bus_data_slot_refcount > 0);
   _dbus_assert (bus_data_slot >= 0);
@@ -118,7 +106,7 @@ data_slot_unref (void)
       bus_data_slot = -1;
     }
 
-  dbus_mutex_unlock (slot_lock);
+  _DBUS_UNLOCK (bus);
 }
 
 static void
index ad8a172..76bbfe4 100644 (file)
@@ -2472,21 +2472,7 @@ dbus_connection_unregister_handler (DBusConnection     *connection,
 }
 
 static DBusDataSlotAllocator slot_allocator;
-
-/**
- * Initialize the mutex used for #DBusConnection data
- * slot reservations.
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_connection_slots_init_lock (void)
-{
-  if (!_dbus_data_slot_allocator_init (&slot_allocator))
-    return NULL;
-  else
-    return slot_allocator.lock;
-}
+_DBUS_DEFINE_GLOBAL_LOCK (connection_slots);
 
 /**
  * Allocates an integer ID to be used for storing application-specific
@@ -2501,7 +2487,8 @@ _dbus_connection_slots_init_lock (void)
 int
 dbus_connection_allocate_data_slot (void)
 {
-  return _dbus_data_slot_allocator_alloc (&slot_allocator);
+  return _dbus_data_slot_allocator_alloc (&slot_allocator,
+                                          _DBUS_LOCK_NAME (connection_slots));
 }
 
 /**
index 46e2bfc..def864e 100644 (file)
@@ -46,11 +46,9 @@ _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
   allocator->allocated_slots = NULL;
   allocator->n_allocated_slots = 0;
   allocator->n_used_slots = 0;
-  allocator->lock = dbus_mutex_new ();
-  if (allocator->lock == NULL)
-    return FALSE;
-  else
-    return TRUE;
+  allocator->lock = NULL;
+  
+  return TRUE;
 }
 
 /**
@@ -62,16 +60,26 @@ _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
  * DBusDataSlotAllocator so it isn't cut-and-pasted everywhere.
  * 
  * @param allocator the allocator
+ * @param mutex the lock for this allocator
  * @returns the integer ID, or -1 on failure
  */
 int
-_dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator)
+_dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
+                                 DBusMutex             *mutex)
 {
   int slot;
-  
-  if (!dbus_mutex_lock (allocator->lock))
+
+  if (!dbus_mutex_lock (mutex))
     return -1;
 
+  if (allocator->n_allocated_slots == 0)
+    {
+      _dbus_assert (allocator->lock == NULL);
+      allocator->lock = mutex;
+    }
+  else
+    _dbus_assert (allocator->lock == mutex);
+  
   if (allocator->n_used_slots < allocator->n_allocated_slots)
     {
       slot = 0;
@@ -128,27 +136,34 @@ _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator)
  */
 void
 _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
-                                int slot)
+                                int                    slot)
 {
   dbus_mutex_lock (allocator->lock);
-
+  
   _dbus_assert (slot < allocator->n_allocated_slots);
   _dbus_assert (allocator->allocated_slots[slot] == slot);
   
   allocator->allocated_slots[slot] = -1;
   allocator->n_used_slots -= 1;
 
+  _dbus_verbose ("Freed slot %d on allocator %p total %d allocated %d used\n",
+                 slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
+  
   if (allocator->n_used_slots == 0)
     {
+      DBusMutex *mutex = allocator->lock;
+      
       dbus_free (allocator->allocated_slots);
       allocator->allocated_slots = NULL;
       allocator->n_allocated_slots = 0;
-    }
+      allocator->lock = NULL;
 
-  _dbus_verbose ("Freed slot %d on allocator %p total %d allocated %d used\n",
-                 slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
-  
-  dbus_mutex_unlock (allocator->lock);
+      dbus_mutex_unlock (mutex);
+    }
+  else
+    {
+      dbus_mutex_unlock (allocator->lock);
+    }
 }
 
 /**
@@ -320,12 +335,17 @@ _dbus_data_slot_test (void)
   int i;
   DBusFreeFunction old_free_func;
   void *old_data;
+  DBusMutex *mutex;
   
   if (!_dbus_data_slot_allocator_init (&allocator))
     _dbus_assert_not_reached ("no memory for allocator");
 
   _dbus_data_slot_list_init (&list);
 
+  mutex = dbus_mutex_new ();
+  if (mutex == NULL)
+    _dbus_assert_not_reached ("failed to alloc mutex");
+  
 #define N_SLOTS 100
 
   i = 0;
@@ -335,7 +355,7 @@ _dbus_data_slot_test (void)
        * allocation, but it simplifies things to rely on it
        * here.
        */
-      if (_dbus_data_slot_allocator_alloc (&allocator) != i)
+      if (_dbus_data_slot_allocator_alloc (&allocator, mutex) != i)
         _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
 
       ++i;
@@ -394,6 +414,8 @@ _dbus_data_slot_test (void)
       _dbus_data_slot_allocator_free (&allocator, i);
       ++i;
     }
+
+  dbus_mutex_free (mutex);
   
   return TRUE;
 }
index 4bb6091..d435732 100644 (file)
@@ -53,23 +53,24 @@ struct DBusDataSlotList
   int           n_slots; /**< Slots we have storage for in data_slots */
 };
 
-dbus_bool_t _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator);
-int         _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator);
-void        _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
-                                            int slot_id);
+dbus_bool_t _dbus_data_slot_allocator_init  (DBusDataSlotAllocator  *allocator);
+int         _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator  *allocator,
+                                             DBusMutex              *mutex);
+void        _dbus_data_slot_allocator_free  (DBusDataSlotAllocator  *allocator,
+                                             int                     slot_id);
+void        _dbus_data_slot_list_init       (DBusDataSlotList       *list);
+dbus_bool_t _dbus_data_slot_list_set        (DBusDataSlotAllocator  *allocator,
+                                             DBusDataSlotList       *list,
+                                             int                     slot,
+                                             void                   *data,
+                                             DBusFreeFunction        free_data_func,
+                                             DBusFreeFunction       *old_free_func,
+                                             void                  **old_data);
+void*       _dbus_data_slot_list_get        (DBusDataSlotAllocator  *allocator,
+                                             DBusDataSlotList       *list,
+                                             int                     slot);
+void        _dbus_data_slot_list_free       (DBusDataSlotList       *list);
 
-void        _dbus_data_slot_list_init (DBusDataSlotList      *list);
-dbus_bool_t _dbus_data_slot_list_set  (DBusDataSlotAllocator *allocator,
-                                       DBusDataSlotList      *list,
-                                       int                    slot,
-                                       void                  *data,
-                                       DBusFreeFunction       free_data_func,
-                                       DBusFreeFunction      *old_free_func,
-                                       void                 **old_data);
-void*       _dbus_data_slot_list_get  (DBusDataSlotAllocator *allocator,
-                                       DBusDataSlotList      *list,
-                                       int                    slot);
-void        _dbus_data_slot_list_free (DBusDataSlotList      *list);
 
 DBUS_END_DECLS;
 
index 5153a76..f9d632b 100644 (file)
  */
 
 /**
+ * @def _DBUS_LOCK_NAME
+ *
+ * Expands to name of a global lock variable.
+ */
+
+/**
+ * @def _DBUS_DEFINE_GLOBAL_LOCK
+ *
+ * Defines a global lock variable with the given name.
+ * The lock must be added to the list to initialize
+ * in dbus_threads_init().
+ */
+
+/**
+ * @def _DBUS_DECLARE_GLOBAL_LOCK
+ *
+ * Expands to declaration of a global lock defined
+ * with _DBUS_DEFINE_GLOBAL_LOCK.
+ * The lock must be added to the list to initialize
+ * in dbus_threads_init().
+ */
+
+/**
+ * @def _DBUS_LOCK
+ *
+ * Locks a global lock
+ */
+
+/**
+ * @def _DBUS_UNLOCK
+ *
+ * Unlocks a global lock
+ */
+
+/**
  * Fixed "out of memory" error message, just to avoid
  * making up a different string every time and wasting
  * space.
index e77c174..72658b6 100644 (file)
@@ -169,10 +169,11 @@ extern const char _dbus_no_memory_message[];
 
 #ifdef DBUS_BUILD_TESTS
 /* Memory debugging */
-void        _dbus_set_fail_alloc_counter       (int  until_next_fail);
-int         _dbus_get_fail_alloc_counter       (void);
-dbus_bool_t _dbus_decrement_fail_alloc_counter (void);
-dbus_bool_t _dbus_disable_mem_pools            (void);
+void        _dbus_set_fail_alloc_counter        (int  until_next_fail);
+int         _dbus_get_fail_alloc_counter        (void);
+dbus_bool_t _dbus_decrement_fail_alloc_counter  (void);
+dbus_bool_t _dbus_disable_mem_pools             (void);
+int         _dbus_get_malloc_blocks_outstanding (void);
 #else
 #define _dbus_set_fail_alloc_counter(n)
 #define _dbus_get_fail_alloc_counter _DBUS_INT_MAX
@@ -180,18 +181,32 @@ dbus_bool_t _dbus_disable_mem_pools            (void);
 /* These are constant expressions so that blocks
  * they protect should be optimized away
  */
-#define _dbus_decrement_fail_alloc_counter() FALSE
-#define _dbus_disable_mem_pools()            FALSE
+#define _dbus_decrement_fail_alloc_counter() (FALSE)
+#define _dbus_disable_mem_pools()            (FALSE)
+#define _dbus_get_malloc_blocks_outstanding  (0)
 #endif /* !DBUS_BUILD_TESTS */
 
+typedef void (* DBusShutdownFunction) (void *data);
+dbus_bool_t _dbus_register_shutdown_func (DBusShutdownFunction  function,
+                                          void                 *data);
+
+extern int _dbus_current_generation;
+
 /* Thread initializers */
-DBusMutex *_dbus_list_init_lock             (void);
-DBusMutex *_dbus_connection_slots_init_lock (void);
-DBusMutex *_dbus_server_slots_init_lock     (void);
-DBusMutex *_dbus_atomic_init_lock           (void);
-DBusMutex *_dbus_message_handler_init_lock  (void);
-DBusMutex *_dbus_user_info_init_lock        (void);
-DBusMutex *_dbus_bus_init_lock              (void);
+#define _DBUS_LOCK_NAME(name)           _dbus_lock_##name
+#define _DBUS_DECLARE_GLOBAL_LOCK(name) extern DBusMutex  *_dbus_lock_##name
+#define _DBUS_DEFINE_GLOBAL_LOCK(name)  DBusMutex         *_dbus_lock_##name  
+#define _DBUS_LOCK(name)                dbus_mutex_lock   (_dbus_lock_##name)
+#define _DBUS_UNLOCK(name)              dbus_mutex_unlock (_dbus_lock_##name)
+
+_DBUS_DECLARE_GLOBAL_LOCK (list);
+_DBUS_DECLARE_GLOBAL_LOCK (connection_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (server_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (atomic);
+_DBUS_DECLARE_GLOBAL_LOCK (message_handler);
+_DBUS_DECLARE_GLOBAL_LOCK (user_info);
+_DBUS_DECLARE_GLOBAL_LOCK (bus);
+#define _DBUS_N_GLOBAL_LOCKS (7)
 
 DBUS_END_DECLS;
 
index 4c530dc..b2efff6 100644 (file)
  */
 
 static DBusMemPool *list_pool;
-static DBusMutex *list_pool_lock = NULL;
-
-/**
- * Initializes the global mutex used for allocating list nodes.
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_list_init_lock (void)
-{
-  list_pool_lock = dbus_mutex_new ();
-  return list_pool_lock;
-}
+_DBUS_DEFINE_GLOBAL_LOCK (list);
 
 /**
  * @defgroup DBusListInternals Linked list implementation details
@@ -67,7 +55,7 @@ alloc_link (void *data)
 {
   DBusList *link;
 
-  if (!dbus_mutex_lock (list_pool_lock))
+  if (!_DBUS_LOCK (list))
     return NULL;
   
   if (!list_pool)
@@ -76,7 +64,7 @@ alloc_link (void *data)
 
       if (list_pool == NULL)
         {
-          dbus_mutex_unlock (list_pool_lock);
+          _DBUS_UNLOCK (list);
           return NULL;
         }
     }
@@ -85,7 +73,7 @@ alloc_link (void *data)
   if (link)
     link->data = data;
   
-  dbus_mutex_unlock (list_pool_lock);
+  _DBUS_UNLOCK (list);
 
   return link;
 }
@@ -93,13 +81,13 @@ alloc_link (void *data)
 static void
 free_link (DBusList *link)
 {
-  dbus_mutex_lock (list_pool_lock);
+  _DBUS_LOCK (list);
   if (_dbus_mem_pool_dealloc (list_pool, link))
     {
       _dbus_mem_pool_free (list_pool);
       list_pool = NULL;
     }
-  dbus_mutex_unlock (list_pool_lock);
+  _DBUS_UNLOCK (list);
 }
 
 static void
index 26766c0..983c6e3 100644 (file)
@@ -24,6 +24,7 @@
 #include "dbus-memory.h"
 #include "dbus-internals.h"
 #include "dbus-sysdeps.h"
+#include "dbus-list.h"
 #include <stdlib.h>
 
 
@@ -81,6 +82,7 @@ static int fail_alloc_counter = _DBUS_INT_MAX;
 static dbus_bool_t guards = FALSE;
 static dbus_bool_t disable_mem_pools = FALSE;
 static dbus_bool_t backtrace_on_fail_alloc = FALSE;
+static int n_blocks_outstanding = 0;
 
 /** value stored in guard padding for debugging buffer overrun */
 #define GUARD_VALUE 0xdeadbeef
@@ -216,6 +218,17 @@ _dbus_decrement_fail_alloc_counter (void)
 }
 
 /**
+ * Get the number of outstanding malloc()'d blocks.
+ *
+ * @returns number of blocks
+ */
+int
+_dbus_get_malloc_blocks_outstanding (void)
+{
+  return n_blocks_outstanding;
+}
+
+/**
  * Where the block came from.
  */
 typedef enum
@@ -372,11 +385,22 @@ dbus_malloc (size_t bytes)
       void *block;
 
       block = malloc (bytes + GUARD_EXTRA_SIZE);
+      if (block)
+        n_blocks_outstanding += 1;
+      
       return set_guards (block, bytes, SOURCE_MALLOC);
     }
 #endif
   else
-    return malloc (bytes);
+    {
+      void *mem;
+      mem = malloc (bytes);
+#ifdef DBUS_BUILD_TESTS
+      if (mem)
+        n_blocks_outstanding += 1;
+#endif
+      return mem;
+    }
 }
 
 /**
@@ -412,11 +436,21 @@ dbus_malloc0 (size_t bytes)
       void *block;
 
       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
+      if (block)
+        n_blocks_outstanding += 1;
       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
     }
 #endif
   else
-    return calloc (bytes, 1);
+    {
+      void *mem;
+      mem = calloc (bytes, 1);
+#ifdef DBUS_BUILD_TESTS
+      if (mem)
+        n_blocks_outstanding += 1;
+#endif
+      return mem;
+    }
 }
 
 /**
@@ -462,9 +496,10 @@ dbus_realloc (void  *memory,
           
           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
                            bytes + GUARD_EXTRA_SIZE);
-          
-          /* old guards shouldn't have moved */
-          check_guards (((unsigned char*)block) + GUARD_START_OFFSET);
+
+          if (block)
+            /* old guards shouldn't have moved */
+            check_guards (((unsigned char*)block) + GUARD_START_OFFSET);
           
           return set_guards (block, bytes, SOURCE_REALLOC);
         }
@@ -473,13 +508,23 @@ dbus_realloc (void  *memory,
           void *block;
           
           block = malloc (bytes + GUARD_EXTRA_SIZE);
+
+          if (block)
+            n_blocks_outstanding += 1;
+          
           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
         }
     }
 #endif
   else
     {
-      return realloc (memory, bytes);
+      void *mem;
+      mem = realloc (memory, bytes);
+#ifdef DBUS_BUILD_TESTS
+      if (memory == NULL && mem != NULL)
+        n_blocks_outstanding += 1;
+#endif
+      return mem;
     }
 }
 
@@ -497,13 +542,28 @@ dbus_free (void  *memory)
     {
       check_guards (memory);
       if (memory)
-        free (((unsigned char*)memory) - GUARD_START_OFFSET);
+        {
+          n_blocks_outstanding -= 1;
+          
+          _dbus_assert (n_blocks_outstanding >= 0);
+          
+          free (((unsigned char*)memory) - GUARD_START_OFFSET);
+        }
+      
       return;
     }
 #endif
     
   if (memory) /* we guarantee it's safe to free (NULL) */
-    free (memory);
+    {
+#ifdef DBUS_BUILD_TESTS
+      n_blocks_outstanding -= 1;
+      
+      _dbus_assert (n_blocks_outstanding >= 0);
+#endif
+
+      free (memory);
+    }
 }
 
 /**
@@ -530,4 +590,89 @@ dbus_free_string_array (char **str_array)
     }
 }
 
+/**
+ * _dbus_current_generation is used to track each
+ * time that dbus_shutdown() is called, so we can
+ * reinit things after it's been called. It is simply
+ * incremented each time we shut down.
+ */
+int _dbus_current_generation = 1;
+
+static DBusList *registered_globals = NULL;
+
+typedef struct
+{
+  DBusShutdownFunction func;
+  void *data;
+} ShutdownClosure;
+
+/**
+ * The D-BUS library keeps some internal global variables, for example
+ * to cache the username of the current process.  This function is
+ * used to free these global variables.  It is really useful only for
+ * leak-checking cleanliness and the like. WARNING: this function is
+ * NOT thread safe, it must be called while NO other threads are using
+ * D-BUS. You cannot continue using D-BUS after calling this function,
+ * as it does things like free global mutexes created by
+ * dbus_threads_init(). To use a D-BUS function after calling
+ * dbus_shutdown(), you have to start over from scratch, e.g. calling
+ * dbus_threads_init() again.
+ */
+void
+dbus_shutdown (void)
+{
+  DBusList *link;
+
+  link = _dbus_list_get_first_link (&registered_globals);
+  while (link != NULL)
+    {
+      ShutdownClosure *c = link->data;
+
+      (* c->func) (c->data);
+
+      dbus_free (c);
+      
+      link = _dbus_list_get_next_link (&registered_globals, link);
+    }
+
+  _dbus_list_clear (&registered_globals);
+
+  _dbus_current_generation += 1;
+}
+
+/**
+ * Register a cleanup function to be called exactly once
+ * the next time dbus_shutdown() is called.
+ *
+ * @param func the function
+ * @param data data to pass to the function
+ * @returns #FALSE on not enough memory
+ */
+dbus_bool_t
+_dbus_register_shutdown_func (DBusShutdownFunction  func,
+                              void                 *data)
+{
+  ShutdownClosure *c;
+
+  c = dbus_new (ShutdownClosure, 1);
+
+  if (c == NULL)
+    return FALSE;
+  
+  c->func = func;
+  c->data = data;
+
+  /* We prepend, then shutdown the list in order, so
+   * we shutdown last-registered stuff first which
+   * is right.
+   */
+  if (!_dbus_list_prepend (&registered_globals, c))
+    {
+      dbus_free (c);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
 /** @} */
index 7a0e316..c0623c8 100644 (file)
@@ -45,6 +45,8 @@ void dbus_free_string_array (char **str_array);
 
 typedef void (* DBusFreeFunction) (void *memory);
 
+void dbus_shutdown (void);
+
 DBUS_END_DECLS;
 
 #endif /* DBUS_MEMORY_H */
index 6d5bb78..fb9eff0 100644 (file)
  * @{
  */
 
-static DBusMutex *message_handler_lock = NULL;
-
-/**
- * Initializes the mutex used for threadsafe access to
- * #DBusMessageHandler objects.
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_message_handler_init_lock (void)
-{
-  message_handler_lock = dbus_mutex_new ();
-  return message_handler_lock;
-}
+_DBUS_DEFINE_GLOBAL_LOCK (message_handler);
 
 /**
  * @brief Internals of DBusMessageHandler
@@ -83,7 +70,7 @@ _dbus_message_handler_add_connection (DBusMessageHandler *handler,
 {
   dbus_bool_t res;
   
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   /* This is a bit wasteful - we just put the connection in the list
    * once per time it's added. :-/
    */
@@ -92,7 +79,7 @@ _dbus_message_handler_add_connection (DBusMessageHandler *handler,
   else
     res = TRUE;
 
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
   
   return res;
 }
@@ -106,10 +93,10 @@ void
 _dbus_message_handler_remove_connection (DBusMessageHandler *handler,
                                          DBusConnection     *connection)
 {
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   if (!_dbus_list_remove (&handler->connections, connection))
     _dbus_warn ("Function _dbus_message_handler_remove_connection() called when the connection hadn't been added\n");
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
 }
 
 
@@ -131,10 +118,10 @@ _dbus_message_handler_handle_message (DBusMessageHandler        *handler,
   DBusHandleMessageFunction function;
   void  *user_data;
   
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   function = handler->function;
   user_data = handler->user_data;
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
   
   /* This function doesn't ref handler/connection/message
    * since that's done in dbus_connection_dispatch().
@@ -205,11 +192,11 @@ dbus_message_handler_new (DBusHandleMessageFunction function,
 void
 dbus_message_handler_ref (DBusMessageHandler *handler)
 {
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   _dbus_assert (handler != NULL);
   
   handler->refcount += 1;
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
 }
 
 /**
@@ -223,7 +210,7 @@ dbus_message_handler_unref (DBusMessageHandler *handler)
 {
   int refcount;
   
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   
   _dbus_assert (handler != NULL);
   _dbus_assert (handler->refcount > 0);
@@ -231,7 +218,7 @@ dbus_message_handler_unref (DBusMessageHandler *handler)
   handler->refcount -= 1;
   refcount = handler->refcount;
   
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
   
   if (refcount == 0)
     {
@@ -267,9 +254,9 @@ void*
 dbus_message_handler_get_data (DBusMessageHandler *handler)
 {
   void* user_data;
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   user_data = handler->user_data;
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
   return user_data;
 }
 
@@ -290,13 +277,13 @@ dbus_message_handler_set_data (DBusMessageHandler *handler,
   DBusFreeFunction old_free_func;
   void *old_user_data;
   
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   old_free_func = handler->free_user_data;
   old_user_data = handler->user_data;
 
   handler->user_data = user_data;
   handler->free_user_data = free_user_data;
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
 
   if (old_free_func)
     (* old_free_func) (old_user_data);
@@ -314,9 +301,9 @@ void
 dbus_message_handler_set_function (DBusMessageHandler        *handler,
                                    DBusHandleMessageFunction  function)
 {
-  dbus_mutex_lock (message_handler_lock);
+  _DBUS_LOCK (message_handler);
   handler->function = function;
-  dbus_mutex_unlock (message_handler_lock);
+  _DBUS_UNLOCK (message_handler);
 }
 
 /** @} */
index ba48cd9..e717096 100644 (file)
@@ -589,21 +589,7 @@ dbus_server_get_n_connections (DBusServer *server)
 
 
 static DBusDataSlotAllocator slot_allocator;
-
-/**
- * Initialize the mutex used for #DBusConnection data
- * slot reservations.
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_server_slots_init_lock (void)
-{
-  if (!_dbus_data_slot_allocator_init (&slot_allocator))
-    return NULL;
-  else
-    return slot_allocator.lock;
-}
+_DBUS_DEFINE_GLOBAL_LOCK (server_slots);
 
 /**
  * Allocates an integer ID to be used for storing application-specific
@@ -618,7 +604,8 @@ _dbus_server_slots_init_lock (void)
 int
 dbus_server_allocate_data_slot (void)
 {
-  return _dbus_data_slot_allocator_alloc (&slot_allocator);
+  return _dbus_data_slot_allocator_alloc (&slot_allocator,
+                                          _DBUS_LOCK_NAME (server_slots));
 }
 
 /**
index 200fbdd..948b083 100644 (file)
@@ -1232,17 +1232,22 @@ _dbus_credentials_from_user_id (unsigned long     user_id,
   return get_user_info (NULL, user_id, credentials, NULL, NULL);
 }
 
-static DBusMutex *user_info_lock = NULL;
-/**
- * Initializes the global mutex for the process's user information.
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_user_info_init_lock (void)
+_DBUS_DEFINE_GLOBAL_LOCK (user_info);
+
+typedef struct
 {
-  user_info_lock = dbus_mutex_new ();
-  return user_info_lock;
+  DBusString name;
+  DBusString dir;
+  DBusCredentials creds;
+} UserInfo;
+
+static void
+shutdown_user_info (void *data)
+{
+  UserInfo *u = data;
+
+  _dbus_string_free (&u->name);
+  _dbus_string_free (&u->dir);
 }
 
 /**
@@ -1258,53 +1263,58 @@ _dbus_user_info_from_current_process (const DBusString      **username,
                                       const DBusString      **homedir,
                                       const DBusCredentials **credentials)
 {
-  static DBusString name;
-  static DBusString dir;
-  static DBusCredentials creds;
-  static dbus_bool_t initialized = FALSE;
+  static UserInfo u;
+  static int initialized_generation = 0;
   
-  if (!dbus_mutex_lock (user_info_lock))
+  if (!_DBUS_LOCK (user_info))
     return FALSE;
 
-  if (!initialized)
+  if (initialized_generation != _dbus_current_generation)
     {
-      if (!_dbus_string_init (&name, _DBUS_INT_MAX))
+      if (!_dbus_string_init (&u.name, _DBUS_INT_MAX))
         {
-          dbus_mutex_unlock (user_info_lock);
+          _DBUS_UNLOCK (user_info);
           return FALSE;
         }
 
-      if (!_dbus_string_init (&dir, _DBUS_INT_MAX))
+      if (!_dbus_string_init (&u.dir, _DBUS_INT_MAX))
         {
-          _dbus_string_free (&name);
-          dbus_mutex_unlock (user_info_lock);
+          _dbus_string_free (&u.name);
+          _DBUS_UNLOCK (user_info);
           return FALSE;
         }
       
-      creds.uid = -1;
-      creds.gid = -1;
-      creds.pid = -1;
+      u.creds.uid = -1;
+      u.creds.gid = -1;
+      u.creds.pid = -1;
 
       if (!get_user_info (NULL, getuid (),
-                          &creds, &dir, &name))
+                          &u.creds, &u.dir, &u.name))
+        goto fail_init;
+      
+      if (!_dbus_register_shutdown_func (shutdown_user_info,
+                                         &u))
+        goto fail_init;
+      
+      initialized_generation = _dbus_current_generation;
+    fail_init:
+      if (initialized_generation != _dbus_current_generation)
         {
-          _dbus_string_free (&name);
-          _dbus_string_free (&dir);
-          dbus_mutex_unlock (user_info_lock);
+          _dbus_string_free (&u.name);
+          _dbus_string_free (&u.dir);
+          _DBUS_UNLOCK (user_info);
           return FALSE;
         }
-
-      initialized = TRUE;
     }
 
   if (username)
-    *username = &name;
+    *username = &u.name;
   if (homedir)
-    *homedir = &dir;
+    *homedir = &u.dir;
   if (credentials)
-    *credentials = &creds;
+    *credentials = &u.creds;
   
-  dbus_mutex_unlock (user_info_lock);
+  _DBUS_UNLOCK (user_info);
 
   return TRUE;
 }
@@ -1594,19 +1604,7 @@ _dbus_string_append_our_uid (DBusString *str)
 }
 
 
-static DBusMutex *atomic_lock = NULL;
-/**
- * Initializes the global mutex for the fallback implementation
- * of atomic integers.
- *
- * @returns the mutex
- */
-DBusMutex *
-_dbus_atomic_init_lock (void)
-{
-  atomic_lock = dbus_mutex_new ();
-  return atomic_lock;
-}
+_DBUS_DEFINE_GLOBAL_LOCK (atomic);
 
 /**
  * Atomically increments an integer
@@ -1621,10 +1619,10 @@ _dbus_atomic_inc (dbus_atomic_t *atomic)
 {
   dbus_atomic_t res;
   
-  dbus_mutex_lock (atomic_lock);
+  _DBUS_LOCK (atomic);
   *atomic += 1;
   res = *atomic;
-  dbus_mutex_unlock (atomic_lock);
+  _DBUS_UNLOCK (atomic);
   return res;
 }
 
@@ -1641,10 +1639,10 @@ _dbus_atomic_dec (dbus_atomic_t *atomic)
 {
   dbus_atomic_t res;
   
-  dbus_mutex_lock (atomic_lock);
+  _DBUS_LOCK (atomic);
   *atomic -= 1;
   res = *atomic;
-  dbus_mutex_unlock (atomic_lock);
+  _DBUS_UNLOCK (atomic);
   return res;
 }
 
index 6275d61..0c607ec 100644 (file)
@@ -39,5 +39,6 @@ main (int    argc,
     test_data_dir = NULL;
   
   dbus_internal_do_not_use_run_tests (test_data_dir);
+  
   return 0;
 }
index 466e8b8..3416011 100644 (file)
 #include <config.h>
 #include "dbus-test.h"
 #include "dbus-sysdeps.h"
+#include "dbus-internals.h"
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef DBUS_BUILD_TESTS
 static void
 die (const char *failure)
 {
   fprintf (stderr, "Unit test failed: %s\n", failure);
   exit (1);
 }
+#endif /* DBUS_BUILD_TESTS */
 
 /**
  * An exported symbol to be run in order to execute
@@ -58,7 +61,7 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
   printf ("%s: running string tests\n", "dbus-test");
   if (!_dbus_string_test ())
     die ("strings");
-
+  
   printf ("%s: running data slot tests\n", "dbus-test");
   if (!_dbus_data_slot_test ())
     die ("dataslot");
@@ -96,7 +99,7 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
   printf ("%s: running memory pool tests\n", "dbus-test");
   if (!_dbus_mem_pool_test ())
     die ("memory pools");
-
+  
   printf ("%s: running linked list tests\n", "dbus-test");
   if (!_dbus_list_test ())
     die ("lists");
@@ -104,11 +107,21 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
   printf ("%s: running hash table tests\n", "dbus-test");
   if (!_dbus_hash_test ())
     die ("hash tables");
-
+  
   printf ("%s: running dict tests\n", "dbus-test");
   if (!_dbus_dict_test ())
     die ("dicts");
 
+  dbus_shutdown ();
+
+  printf ("%s: checking for memleaks\n", "dbus-test");
+  if (_dbus_get_malloc_blocks_outstanding () != 0)
+    {
+      _dbus_warn ("%d dbus_malloc blocks were not freed\n",
+                  _dbus_get_malloc_blocks_outstanding ());
+      die ("memleaks");
+    }
+  
   printf ("%s: completed successfully\n", "dbus-test");
 #else
   printf ("Not compiled with unit tests, not running any\n");
index ec83180..d481479 100644 (file)
@@ -32,12 +32,21 @@ static DBusThreadFunctions thread_functions =
   NULL, NULL, NULL, NULL,
   NULL, NULL, NULL, NULL
 };
+static int thread_init_generation = 0;
 
 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
-#define _DBUS_DUMMY_MUTEX ((void*)0xABCDEF)
+#ifdef DBUS_BUILD_TESTS
+#define _DBUS_DUMMY_MUTEX_NEW ((DBusMutex*)_dbus_strdup ("FakeMutex"))
+#else
+#define _DBUS_DUMMY_MUTEX_NEW ((DBusMutex*)0xABCDEF)
+#endif
 
 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
-#define _DBUS_DUMMY_CONDVAR ((void*)0xABCDEF2)
+#ifdef DBUS_BUILD_TESTS
+#define _DBUS_DUMMY_CONDVAR_NEW ((DBusCondVar*)_dbus_strdup ("FakeCondvar"))
+#else
+#define _DBUS_DUMMY_CONDVAR_NEW ((DBusCondVar*)0xABCDEF2)
+#endif
 
 /**
  * @defgroup DBusThreads Thread functions
@@ -63,7 +72,7 @@ dbus_mutex_new (void)
   if (thread_functions.mutex_new)
     return (* thread_functions.mutex_new) ();
   else
-    return _DBUS_DUMMY_MUTEX;
+    return _DBUS_DUMMY_MUTEX_NEW;
 }
 
 /**
@@ -75,6 +84,11 @@ dbus_mutex_free (DBusMutex *mutex)
 {
   if (mutex && thread_functions.mutex_free)
     (* thread_functions.mutex_free) (mutex);
+#ifdef DBUS_BUILD_TESTS
+  /* Free the fake mutex */
+  else
+    dbus_free (mutex);
+#endif
 }
 
 /**
@@ -120,7 +134,7 @@ dbus_condvar_new (void)
   if (thread_functions.condvar_new)
     return (* thread_functions.condvar_new) ();
   else
-    return _DBUS_DUMMY_MUTEX;
+    return _DBUS_DUMMY_CONDVAR_NEW;
 }
 
 /**
@@ -132,6 +146,11 @@ dbus_condvar_free (DBusCondVar *cond)
 {
   if (cond && thread_functions.condvar_free)
     (* thread_functions.condvar_free) (cond);
+#ifdef DBUS_BUILD_TESTS
+  else
+    /* Free the fake condvar */
+    dbus_free (cond);
+#endif
 }
 
 /**
@@ -195,37 +214,77 @@ dbus_condvar_wake_all (DBusCondVar *cond)
     (* thread_functions.condvar_wake_all) (cond);
 }
 
+static void
+shutdown_global_locks (void *data)
+{
+  DBusMutex ***locks = data;
+  int i;
+
+  i = 0;
+  while (i < _DBUS_N_GLOBAL_LOCKS)
+    {
+      dbus_mutex_free (*(locks[i]));
+      *(locks[i]) = NULL;
+      ++i;
+    }
+  
+  dbus_free (locks);
+}
+
 static dbus_bool_t
-init_static_locks(void)
+init_global_locks (void)
 {
   int i;
+  DBusMutex ***dynamic_global_locks;
   
-  struct {
-    DBusMutex *(*init_func)(void);
-    DBusMutex *mutex;
-  } static_locks[] = {
-    {&_dbus_list_init_lock},
-    {&_dbus_server_slots_init_lock},
-    {&_dbus_connection_slots_init_lock},
-    {&_dbus_atomic_init_lock},
-    {&_dbus_message_handler_init_lock},
-    {&_dbus_user_info_init_lock},
-    {&_dbus_bus_init_lock}
+  DBusMutex **global_locks[] = {
+#define LOCK_ADDR(name) (& _dbus_lock_##name)
+    LOCK_ADDR (list),
+    LOCK_ADDR (connection_slots),
+    LOCK_ADDR (server_slots),
+    LOCK_ADDR (atomic),
+    LOCK_ADDR (message_handler),
+    LOCK_ADDR (user_info),
+    LOCK_ADDR (bus)
+#undef LOCK_ADDR
   };
+
+  _dbus_assert (_DBUS_N_ELEMENTS (global_locks) ==
+                _DBUS_N_GLOBAL_LOCKS);
+
+  i = 0;
   
-  for (i = 0; i < _DBUS_N_ELEMENTS (static_locks); i++)
+  dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS);
+  if (dynamic_global_locks == NULL)
+    goto failed;
+  
+  while (i < _DBUS_N_ELEMENTS (global_locks))
     {
-      static_locks[i].mutex = (*static_locks[i].init_func)();
-      
-      if (static_locks[i].mutex == NULL)
-       {
-         for (i = i - 1; i >= 0; i--)
-           dbus_mutex_free (static_locks[i].mutex);
-         return FALSE;
-       }
+      *global_locks[i] = dbus_mutex_new ();
       
+      if (*global_locks[i] == NULL)
+        goto failed;
+
+      dynamic_global_locks[i] = global_locks[i];
+
+      ++i;
     }
+  
+  if (!_dbus_register_shutdown_func (shutdown_global_locks,
+                                     dynamic_global_locks))
+    goto failed;
+  
   return TRUE;
+
+ failed:
+  dbus_free (dynamic_global_locks);
+                                     
+  for (i = i - 1; i >= 0; i--)
+    {
+      dbus_mutex_free (*global_locks[i]);
+      *global_locks[i] = NULL;
+    }
+  return FALSE;
 }
 
 
@@ -276,6 +335,9 @@ dbus_threads_init (const DBusThreadFunctions *functions)
    * new bits.
    */
   _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
+
+  if (thread_init_generation != _dbus_current_generation)
+    thread_functions.mask = 0; /* allow re-init in new generation */
   
   if (thread_functions.mask != 0)
     {
@@ -297,8 +359,10 @@ dbus_threads_init (const DBusThreadFunctions *functions)
   
   thread_functions.mask = functions->mask;
 
-  if (!init_static_locks ())
+  if (!init_global_locks ())
     return FALSE;
+
+  thread_init_generation = _dbus_current_generation;
   
   return TRUE;
 }