diff options
Diffstat (limited to 'dbus/dbus-threads.c')
| -rw-r--r-- | dbus/dbus-threads.c | 212 | 
1 files changed, 193 insertions, 19 deletions
diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index f22d0a68..78269634 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -23,6 +23,7 @@  #include "dbus-threads.h"  #include "dbus-internals.h"  #include "dbus-threads-internal.h" +#include "dbus-list.h"  static DBusThreadFunctions thread_functions =  { @@ -35,6 +36,9 @@ static DBusThreadFunctions thread_functions =  };  static int thread_init_generation = 0; +static DBusList *uninitialized_mutex_list = NULL; +static DBusList *uninitialized_condvar_list = NULL; +  /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */  #define _DBUS_DUMMY_MUTEX ((DBusMutex*)0xABCDEF) @@ -69,6 +73,32 @@ _dbus_mutex_new (void)  }  /** + * This does the same thing as _dbus_mutex_new.  It however + * gives another level of indirection by allocating a pointer + * to point to the mutex location.  This allows the threading + * module to swap out dummy mutexes for real a real mutex so libraries + * can initialize threads even after the D-Bus API has been used. + * + * @param location_p the location of the new mutex, can return #NULL on OOM + */ +void +_dbus_mutex_new_at_location (DBusMutex **location_p) +{ +  _dbus_assert (location_p != NULL); + +  *location_p = _dbus_mutex_new(); + +  if (thread_init_generation != _dbus_current_generation && *location_p) +    { +      if (!_dbus_list_append (&uninitialized_mutex_list, location_p)) +        { +	  _dbus_mutex_free (*location_p); +	  *location_p = NULL; +	} +    } +} + +/**   * Frees a mutex created with dbus_mutex_new(); does   * nothing if passed a #NULL pointer.   */ @@ -80,6 +110,23 @@ _dbus_mutex_free (DBusMutex *mutex)  }  /** + * Frees a mutex and removes it from the  + * uninitialized_mutex_list; + * does nothing if passed a #NULL pointer. + */ +void +_dbus_mutex_free_at_location (DBusMutex **location_p) +{ +  if (location_p) +    { +      if (thread_init_generation != _dbus_current_generation) +        _dbus_list_remove (&uninitialized_mutex_list, location_p); + +      _dbus_mutex_free (*location_p); +    } +} + +/**   * Locks a mutex. Does nothing if passed a #NULL pointer.   * Locks are not recursive.   * @@ -125,6 +172,33 @@ _dbus_condvar_new (void)      return _DBUS_DUMMY_CONDVAR;  } + +/** + * This does the same thing as _dbus_condvar_new.  It however + * gives another level of indirection by allocating a pointer + * to point to the condvar location.  This allows the threading + * module to swap out dummy condvars for real a real condvar so libraries + * can initialize threads even after the D-Bus API has been used. + * + * @returns the location of a new condvar or #NULL on OOM + */ + +void  +_dbus_condvar_new_at_location (DBusCondVar **location_p) +{ +  *location_p = _dbus_condvar_new(); + +  if (thread_init_generation != _dbus_current_generation && *location_p) +    { +      if (!_dbus_list_append (&uninitialized_condvar_list, location_p)) +        { +          _dbus_condvar_free (*location_p); +          *location_p = NULL; +        } +    } +} + +  /**   * Frees a conditional variable created with dbus_condvar_new(); does   * nothing if passed a #NULL pointer. @@ -137,6 +211,23 @@ _dbus_condvar_free (DBusCondVar *cond)  }  /** + * Frees a conditional variable and removes it from the  + * uninitialized_condvar_list;  + * does nothing if passed a #NULL pointer. + */ +void +_dbus_condvar_free_at_location (DBusCondVar **location_p) +{ +  if (location_p) +    { +      if (thread_init_generation != _dbus_current_generation) +        _dbus_list_remove (&uninitialized_condvar_list, location_p); + +      _dbus_condvar_free (*location_p); +    } +} + +/**   * Atomically unlocks the mutex and waits for the conditions   * variable to be signalled. Locks the mutex again before   * returning. @@ -214,8 +305,100 @@ shutdown_global_locks (void *data)    dbus_free (locks);  } +static void +shutdown_uninitialized_locks (void *data) +{ +  _dbus_list_clear (&uninitialized_mutex_list); +  _dbus_list_clear (&uninitialized_condvar_list); +} +  static dbus_bool_t -init_global_locks (void) +init_uninitialized_locks (void) +{ +  DBusList *link; + +  _dbus_assert (thread_init_generation == 0); + +  link = uninitialized_mutex_list; +  while (link != NULL) +    { +      DBusMutex **mp; + +      mp = (DBusMutex **)link->data; +      _dbus_assert (*mp == _DBUS_DUMMY_MUTEX); + +      *mp = _dbus_mutex_new (); +      if (*mp == NULL) +        goto fail_mutex; + +      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link); +    } + +  link = uninitialized_condvar_list; +  while (link != NULL) +    { +      DBusCondVar **cp; + +      cp = (DBusCondVar **)link->data; +      _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR); + +      *cp = _dbus_condvar_new (); +      if (*cp == NULL) +        goto fail_condvar; + +      link = _dbus_list_get_next_link (&uninitialized_condvar_list, link); +    } + +  _dbus_list_clear (&uninitialized_mutex_list); +  _dbus_list_clear (&uninitialized_condvar_list); + +  if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks, +                                     NULL)) +    goto fail_condvar; + +  return TRUE; + + fail_condvar: +  link = uninitialized_condvar_list; +  while (link != NULL) +    { +      DBusCondVar **cp; + +      cp = (DBusCondVar **)link->data; + +      if (*cp != _DBUS_DUMMY_CONDVAR) +        _dbus_condvar_free (*cp); +      else +        break; + +      *cp = _DBUS_DUMMY_CONDVAR; + +      link = _dbus_list_get_next_link (&uninitialized_condvar_list, link); +    } + + fail_mutex: +  link = uninitialized_mutex_list; +  while (link != NULL) +    { +      DBusMutex **mp; + +      mp = (DBusMutex **)link->data; + +      if (*mp != _DBUS_DUMMY_MUTEX) +        _dbus_mutex_free (*mp); +      else +        break; + +      *mp = _DBUS_DUMMY_MUTEX; + +      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link); +    } + +  return FALSE; +} + +static dbus_bool_t +init_locks (void)  {    int i;    DBusMutex ***dynamic_global_locks; @@ -260,6 +443,9 @@ init_global_locks (void)    if (!_dbus_register_shutdown_func (shutdown_global_locks,                                       dynamic_global_locks))      goto failed; + +  if (!init_uninitialized_locks ()) +    goto failed;    return TRUE; @@ -339,24 +525,12 @@ dbus_threads_init (const DBusThreadFunctions *functions)    if (thread_init_generation != _dbus_current_generation)      thread_functions.mask = 0; /* allow re-init in new generation */ -   +  +  /* Silently allow multiple init +   * First init wins and D-Bus will always use its threading system  +   */     if (thread_functions.mask != 0) -    { -      /* Silently allow multiple init if the functions are the same ones. -       * Well, we only bother checking two of them, just out of laziness. -       */ -      if (thread_functions.mask == functions->mask && -          thread_functions.mutex_new == functions->mutex_new && -          thread_functions.condvar_new == functions->condvar_new) -        { -          return TRUE; -        } -      else -        { -          _dbus_warn ("dbus_threads_init() called twice with two different sets of functions\n"); -          return FALSE; -        } -    } +    return TRUE;    thread_functions.mutex_new = functions->mutex_new;    thread_functions.mutex_free = functions->mutex_free; @@ -372,7 +546,7 @@ dbus_threads_init (const DBusThreadFunctions *functions)    thread_functions.mask = functions->mask; -  if (!init_global_locks ()) +  if (!init_locks ())      return FALSE;    thread_init_generation = _dbus_current_generation;  | 
