Commit c3af5ccd authored by Havoc Pennington's avatar Havoc Pennington

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
parent a26607ab
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
......
......@@ -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);
......
......@@ -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;
......
......@@ -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
......
......@@ -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));
}
/**
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -127,6 +127,41 @@
* a DBusList.
*/
/**
* @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
......
......@@ -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;
......
......@@ -35,19 +35,7 @@
*/
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
......
......@@ -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
......@@ -215,6 +217,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.
*/
......@@ -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;