2003-08-06 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Thu, 7 Aug 2003 02:18:54 +0000 (02:18 +0000)
committerHavoc Pennington <hp@redhat.com>
Thu, 7 Aug 2003 02:18:54 +0000 (02:18 +0000)
* dbus/dbus-object-registry.c: implement signal connection
and dispatch

* dbus/dbus-connection.c (_dbus_connection_unref_unlocked): new

* dbus/dbus-internals.c (_dbus_memdup): new function

ChangeLog
dbus/dbus-connection-internal.h
dbus/dbus-connection.c
dbus/dbus-internals.c
dbus/dbus-internals.h
dbus/dbus-object-registry.c
dbus/dbus-object-registry.h
dbus/dbus-object.h

index a505138..850906c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2003-08-06  Havoc Pennington  <hp@pobox.com>
+
+       * dbus/dbus-object-registry.c: implement signal connection 
+       and dispatch
+
+       * dbus/dbus-connection.c (_dbus_connection_unref_unlocked): new
+
+       * dbus/dbus-internals.c (_dbus_memdup): new function
+
 2003-08-02  Havoc Pennington  <hp@pobox.com>
 
        * dbus/dbus-message.c (dbus_message_get_no_reply)
index 5bcbcc2..423df5f 100644 (file)
@@ -42,6 +42,7 @@ typedef enum
 void              _dbus_connection_lock                        (DBusConnection     *connection);
 void              _dbus_connection_unlock                      (DBusConnection     *connection);
 void              _dbus_connection_ref_unlocked                (DBusConnection     *connection);
+void              _dbus_connection_unref_unlocked              (DBusConnection     *connection);
 dbus_bool_t       _dbus_connection_queue_received_message      (DBusConnection     *connection,
                                                                 DBusMessage        *message);
 void              _dbus_connection_queue_received_message_link (DBusConnection     *connection,
index ced50bd..407b4d2 100644 (file)
@@ -203,7 +203,7 @@ static void               _dbus_connection_remove_timeout_locked             (DB
 static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked      (DBusConnection     *connection);
 static void               _dbus_connection_update_dispatch_status_and_unlock (DBusConnection     *connection,
                                                                               DBusDispatchStatus  new_status);
-
+static void               _dbus_connection_last_unref                        (DBusConnection     *connection);
 
 
 /**
@@ -824,6 +824,39 @@ _dbus_connection_ref_unlocked (DBusConnection *connection)
 #endif
 }
 
+/**
+ * Decrements the reference count of a DBusConnection.
+ * Requires that the caller already holds the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_unref_unlocked (DBusConnection *connection)
+{
+  dbus_bool_t last_unref;
+
+  _dbus_return_if_fail (connection != NULL);
+
+  /* The connection lock is better than the global
+   * lock in the atomic increment fallback
+   */
+  
+#ifdef DBUS_HAVE_ATOMIC_INT
+  last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
+#else  
+  _dbus_assert (connection->refcount.value > 0);
+
+  connection->refcount.value -= 1;
+  last_unref = (connection->refcount.value == 0);
+#if 0
+  printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value);
+#endif
+#endif
+  
+  if (last_unref)
+    _dbus_connection_last_unref (connection);
+}
+
 static dbus_uint32_t
 _dbus_connection_get_next_client_serial (DBusConnection *connection)
 {
@@ -2215,6 +2248,8 @@ dbus_connection_get_dispatch_status (DBusConnection *connection)
  * does not necessarily dispatch a message, as the data may
  * be part of authentication or the like.
  *
+ * @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY
+ * 
  * @param connection the connection
  * @returns dispatch status
  */
@@ -2310,7 +2345,7 @@ dbus_connection_dispatch (DBusConnection *connection)
       result = _dbus_message_handler_handle_message (handler, connection,
                                                      message);
 
-      if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
+      if (result != DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS)
        break;
 
       link = next;
@@ -2323,6 +2358,9 @@ dbus_connection_dispatch (DBusConnection *connection)
   
   CONNECTION_LOCK (connection);
 
+  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+    /* FIXME */ ;
+  
   /* Did a reply we were waiting on get filtered? */
   if (reply_handler_data && result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
     {
@@ -2342,7 +2380,7 @@ dbus_connection_dispatch (DBusConnection *connection)
   
   if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
     goto out;
-
+  
   if (reply_handler_data)
     {
       CONNECTION_UNLOCK (connection);
@@ -2364,9 +2402,13 @@ dbus_connection_dispatch (DBusConnection *connection)
   
   result = _dbus_object_registry_handle_and_unlock (connection->objects,
                                                     message);
+  
   CONNECTION_LOCK (connection);
   if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
     goto out;
+
+  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+    /* FIXME */ ; 
   
   _dbus_verbose ("  done dispatching %p (%s) on connection %p\n", message,
                  dbus_message_get_name (message), connection);
index 6e7f9e1..ccc1177 100644 (file)
@@ -248,7 +248,7 @@ _dbus_verbose_reset_real (void)
 char*
 _dbus_strdup (const char *str)
 {
-  int len;
+  size_t len;
   char *copy;
   
   if (str == NULL)
@@ -265,6 +265,31 @@ _dbus_strdup (const char *str)
   return copy;
 }
 
+#ifdef DBUS_BUILD_TESTS /* memdup not used at the moment */
+/**
+ * Duplicates a block of memory. Returns
+ * #NULL on failure.
+ *
+ * @param mem memory to copy
+ * @param n_bytes number of bytes to copy
+ * @returns the copy
+ */
+void*
+_dbus_memdup (const void  *mem,
+              size_t       n_bytes)
+{
+  void *copy;
+
+  copy = dbus_malloc (n_bytes);
+  if (copy == NULL)
+    return NULL;
+
+  memcpy (copy, mem, n_bytes);
+  
+  return copy;
+}
+#endif
+
 /**
  * Duplicates a string array. Result may be freed with
  * dbus_free_string_array(). Returns #NULL if memory allocation fails.
index 7acda71..6d120f1 100644 (file)
@@ -144,6 +144,8 @@ extern const char _dbus_return_if_fail_warning_format[];
   ((void*)_DBUS_ALIGN_VALUE(this, boundary))
 
 char*       _dbus_strdup                (const char  *str);
+void*       _dbus_memdup                (const void  *mem,
+                                         size_t       n_bytes);
 dbus_bool_t _dbus_string_array_contains (const char **array,
                                          const char  *str);
 char**      _dbus_dup_string_array      (const char **array);
index a4d9221..55f8f74 100644 (file)
  * Types and functions related to DBusObjectRegistry. These
  * are all internal.
  *
+ * @todo interface entries and signal connections are handled pretty
+ * much identically, with lots of duplicate code.  Once we're sure
+ * they will always be the same, we could merge this code.
+ *
  * @{
  */
 
 typedef struct DBusObjectEntry DBusObjectEntry;
 typedef struct DBusInterfaceEntry DBusInterfaceEntry;
+typedef struct DBusSignalEntry DBusSignalEntry;
 
 #define DBUS_MAX_OBJECTS_PER_INTERFACE 65535
 struct DBusInterfaceEntry
@@ -50,6 +55,17 @@ struct DBusInterfaceEntry
   char name[4];                  /**< Name of interface (actually allocated larger) */
 };
 
+#define DBUS_MAX_CONNECTIONS_PER_SIGNAL 65535
+struct DBusSignalEntry
+{
+  unsigned int n_connections : 16; /**< Number of connections to this signal */
+  unsigned int n_allocated : 16;   /**< Allocated size of objects array */
+  dbus_uint16_t *connections;      /**< Index of each object connected (can have dups for multiple
+                                    * connections)
+                                    */
+  char name[4];                    /**< Name of signal (actually allocated larger) */
+};
+
  /* 14 bits for object index, 32K objects */
 #define DBUS_OBJECT_INDEX_BITS          (14)
 #define DBUS_OBJECT_INDEX_MASK          (0x3fff)
@@ -62,6 +78,7 @@ struct DBusObjectEntry
   void *object_impl;               /**< Pointer to application-supplied implementation */
   const DBusObjectVTable *vtable;  /**< Virtual table for this object */
   DBusInterfaceEntry **interfaces; /**< NULL-terminated list of interfaces */
+  DBusSignalEntry    **signals;    /**< Signal connections (contains dups, one each time we connect) */
 };
 
 struct DBusObjectRegistry
@@ -74,6 +91,8 @@ struct DBusObjectRegistry
   int n_entries_used;
 
   DBusHashTable *interface_table;
+
+  DBusHashTable *signal_table;
 };
 
 static void
@@ -88,11 +107,24 @@ free_interface_entry (void *entry)
   dbus_free (iface);
 }
 
+static void
+free_signal_entry (void *entry)
+{
+  DBusSignalEntry *signal = entry;
+
+  if (signal == NULL) /* DBusHashTable stupidity */
+    return;
+  
+  dbus_free (signal->connections);
+  dbus_free (signal);
+}
+
 DBusObjectRegistry*
 _dbus_object_registry_new (DBusConnection *connection)
 {
   DBusObjectRegistry *registry;
   DBusHashTable *interface_table;
+  DBusHashTable *signal_table;
   
   /* the connection passed in here isn't fully constructed,
    * so don't do anything more than store a pointer to
@@ -101,6 +133,7 @@ _dbus_object_registry_new (DBusConnection *connection)
 
   registry = NULL;
   interface_table = NULL;
+  signal_table = NULL;
   
   registry = dbus_new0 (DBusObjectRegistry, 1);
   if (registry == NULL)
@@ -110,10 +143,16 @@ _dbus_object_registry_new (DBusConnection *connection)
                                           NULL, free_interface_entry);
   if (interface_table == NULL)
     goto oom;
+
+  signal_table = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                          NULL, free_signal_entry);
+  if (signal_table == NULL)
+    goto oom;
   
   registry->refcount = 1;
   registry->connection = connection;
   registry->interface_table = interface_table;
+  registry->signal_table = signal_table;
   
   return registry;
 
@@ -122,7 +161,9 @@ _dbus_object_registry_new (DBusConnection *connection)
     dbus_free (registry);
   if (interface_table)
     _dbus_hash_table_unref (interface_table);
-
+  if (signal_table)
+    _dbus_hash_table_unref (signal_table);
+  
   return NULL;
 }
 
@@ -147,16 +188,20 @@ _dbus_object_registry_unref (DBusObjectRegistry *registry)
       
       _dbus_assert (registry->n_entries_used == 0);
       _dbus_assert (_dbus_hash_table_get_n_entries (registry->interface_table) == 0);
+      _dbus_assert (_dbus_hash_table_get_n_entries (registry->signal_table) == 0);
 
       i = 0;
       while (i < registry->n_entries_allocated)
         {
           if (registry->entries[i].interfaces)
             dbus_free (registry->entries[i].interfaces);
+          if (registry->entries[i].signals)
+            dbus_free (registry->entries[i].signals);
           ++i;
         }
       
       _dbus_hash_table_unref (registry->interface_table);
+      _dbus_hash_table_unref (registry->signal_table);
       dbus_free (registry->entries);
       dbus_free (registry);
     }
@@ -213,32 +258,41 @@ validate_id (DBusObjectRegistry *registry,
 }
 
 static void
-info_from_entry (DBusObjectRegistry *registry,
-                 DBusObjectInfo     *info,
-                 DBusObjectEntry    *entry)
+id_from_entry (DBusObjectRegistry *registry,
+               DBusObjectID       *object_id,
+               DBusObjectEntry    *entry)
 {
-  info->connection = registry->connection;
-  info->object_impl = entry->object_impl;
 #ifdef DBUS_BUILD_TESTS
   if (registry->connection)
 #endif
     _dbus_connection_init_id (registry->connection,
-                              &info->object_id);
+                              object_id);
 #ifdef DBUS_BUILD_TESTS
   else
     {
-      dbus_object_id_set_server_bits (&info->object_id, 1);
-      dbus_object_id_set_client_bits (&info->object_id, 2);
+      dbus_object_id_set_server_bits (object_id, 1);
+      dbus_object_id_set_client_bits (object_id, 2);
     }
 #endif
 
-  _dbus_assert (dbus_object_id_get_server_bits (&info->object_id) != 0);
-  _dbus_assert (dbus_object_id_get_client_bits (&info->object_id) != 0);
+  _dbus_assert (dbus_object_id_get_server_bits (object_id) != 0);
+  _dbus_assert (dbus_object_id_get_client_bits (object_id) != 0);
   
-  dbus_object_id_set_instance_bits (&info->object_id,
+  dbus_object_id_set_instance_bits (object_id,
                                     ENTRY_TO_ID (entry));
 
-  _dbus_assert (dbus_object_id_get_instance_bits (&info->object_id) != 0);
+  _dbus_assert (dbus_object_id_get_instance_bits (object_id) != 0);
+}
+
+static void
+info_from_entry (DBusObjectRegistry *registry,
+                 DBusObjectInfo     *info,
+                 DBusObjectEntry    *entry)
+{
+  info->connection = registry->connection;
+  info->object_impl = entry->object_impl;
+
+  id_from_entry (registry, &info->object_id, entry);
 }
 
 static DBusInterfaceEntry*
@@ -375,6 +429,483 @@ object_remove_from_interfaces (DBusObjectRegistry *registry,
     }
 }
 
+static DBusSignalEntry*
+lookup_signal (DBusObjectRegistry *registry,
+               const char         *name,
+               dbus_bool_t         create_if_not_found)
+{
+  DBusSignalEntry *entry;
+  int sz;
+  int len;
+  
+  entry = _dbus_hash_table_lookup_string (registry->signal_table,
+                                          name);
+  if (entry != NULL || !create_if_not_found)
+    return entry;
+  
+  _dbus_assert (create_if_not_found);
+
+  len = strlen (name);
+  sz = _DBUS_STRUCT_OFFSET (DBusSignalEntry, name) + len + 1;
+  entry = dbus_malloc (sz);
+  if (entry == NULL)
+    return NULL;
+  entry->n_connections = 0;
+  entry->n_allocated = 0;
+  entry->connections = NULL;
+  memcpy (entry->name, name, len + 1);
+
+  if (!_dbus_hash_table_insert_string (registry->signal_table,
+                                       entry->name, entry))
+    {
+      dbus_free (entry);
+      return NULL;
+    }
+  
+  return entry;
+}
+
+static void
+delete_signal (DBusObjectRegistry *registry,
+               DBusSignalEntry *entry)
+{
+  _dbus_hash_table_remove_string (registry->signal_table,
+                                  entry->name);
+}
+
+static dbus_bool_t
+signal_entry_add_object (DBusSignalEntry *entry,
+                         dbus_uint16_t    object_index)
+{
+  if (entry->n_connections == entry->n_allocated)
+    {
+      unsigned int new_alloc;
+      dbus_uint16_t *new_objects;
+      
+      if (entry->n_allocated == 0)
+        new_alloc = 2;
+      else
+        new_alloc = entry->n_allocated * 2;
+
+      /* Right now MAX_CONNECTIONS_PER_SIGNAL can't possibly be reached
+       * since the max number of objects _total_ is smaller, but the
+       * code is here for future robustness.
+       */
+      
+      if (new_alloc > DBUS_MAX_CONNECTIONS_PER_SIGNAL)
+        new_alloc = DBUS_MAX_CONNECTIONS_PER_SIGNAL;
+      if (new_alloc == entry->n_allocated)
+        {
+          _dbus_warn ("Attempting to register another instance with signal %s, but max count %d reached\n",
+                      entry->name, DBUS_MAX_CONNECTIONS_PER_SIGNAL);
+          return FALSE;
+        }
+
+      new_objects = dbus_realloc (entry->connections, new_alloc * sizeof (dbus_uint16_t));
+      if (new_objects == NULL)
+        return FALSE;
+      entry->connections = new_objects;
+      entry->n_allocated = new_alloc;
+    }
+
+  _dbus_assert (entry->n_connections < entry->n_allocated);
+
+  entry->connections[entry->n_connections] = object_index;
+  entry->n_connections += 1;
+
+  return TRUE;
+}
+
+static void
+signal_entry_remove_object (DBusSignalEntry *entry,
+                            dbus_uint16_t    object_index)
+{
+  unsigned int i;
+
+  i = 0;
+  while (i < entry->n_connections)
+    {
+      if (entry->connections[i] == object_index)
+        break;
+      ++i;
+    }
+
+  if (i == entry->n_connections)
+    {
+      _dbus_assert_not_reached ("Tried to remove object from an signal that didn't list that object\n");
+      return;
+    }
+
+  memmove (&entry->connections[i],
+           &entry->connections[i+1],
+           (entry->n_connections - i - 1) * sizeof (entry->connections[0]));
+  entry->n_connections -= 1;  
+}
+
+static void
+object_remove_from_signals (DBusObjectRegistry *registry,
+                            DBusObjectEntry    *entry)
+{
+  if (entry->signals != NULL)
+    {
+      int i;
+      
+      i = 0;
+      while (entry->signals[i] != NULL)
+        {
+          DBusSignalEntry *iface = entry->signals[i];
+          
+          signal_entry_remove_object (iface, entry->id_index);
+          if (iface->n_connections == 0)
+            delete_signal (registry, iface);
+          ++i;
+        }
+    }
+}
+
+/**
+ * Connect this object to the given signal, such that if a
+ * signal emission message is received with the given
+ * signal name, the message will be routed to the
+ * given object.
+ *
+ * Must be called with #DBusConnection lock held.
+ * 
+ * @param registry the object registry
+ * @param object_id object that would like to see the signal
+ * @param signal signal name
+ *
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_object_registry_connect_locked (DBusObjectRegistry *registry,
+                                      const DBusObjectID *object_id,
+                                      const char         *signal_name)
+{
+  DBusSignalEntry **new_signals;
+  DBusSignalEntry *signal;
+  DBusObjectEntry *entry;
+  int i;
+  
+  entry = validate_id (registry, object_id);
+  if (entry == NULL)
+    {
+      _dbus_warn ("Tried to connect a nonexistent D-BUS object ID to signal \"%s\"\n",
+                  signal_name);
+      
+      return FALSE;
+    }
+
+  /* O(n) in number of connections unfortunately, but in practice I
+   * don't think it will matter.  It's marginally a space-time
+   * tradeoff (save an n_signals field) but the NULL termination is
+   * just as large as an n_signals once we have even a single
+   * connection.
+   */
+  i = 0;
+  if (entry->signals != NULL)
+    {
+      while (entry->signals[i] != NULL)
+        ++i;
+    }
+  
+  new_signals = dbus_realloc (entry->signals,
+                              (i + 2) * sizeof (DBusSignalEntry*));
+  
+  if (new_signals == NULL)
+    return FALSE;
+
+  entry->signals = new_signals;
+  
+  signal = lookup_signal (registry, signal_name, TRUE); 
+  if (signal == NULL)
+    goto oom;
+
+  if (!signal_entry_add_object (signal, entry->id_index))
+    goto oom;
+  
+  entry->signals[i] = signal;
+  ++i;
+  entry->signals[i] = NULL;
+
+  return TRUE;
+  
+ oom:
+  if (signal && signal->n_connections == 0)
+    delete_signal (registry, signal);
+  
+  return FALSE;
+}
+
+/**
+ * Reverses effects of _dbus_object_registry_disconnect_locked().
+ *
+ * @param registry the object registry
+ * @param object_id object that would like to see the signal
+ * @param signal signal name
+ */
+void
+_dbus_object_registry_disconnect_locked (DBusObjectRegistry      *registry,
+                                         const DBusObjectID      *object_id,
+                                         const char              *signal_name)
+{
+  DBusObjectEntry *entry;
+  DBusSignalEntry *signal;
+  
+  entry = validate_id (registry, object_id);
+  if (entry == NULL)
+    {
+      _dbus_warn ("Tried to disconnect signal \"%s\" from a nonexistent D-BUS object ID\n",
+                  signal_name);
+      
+      return;
+    }
+
+  signal = lookup_signal (registry, signal_name, FALSE);
+  if (signal == NULL)
+    {
+      _dbus_warn ("Tried to disconnect signal \"%s\" but no such signal is connected\n",
+                  signal_name);
+      return;
+    }
+  
+  signal_entry_remove_object (signal, entry->id_index);
+
+  if (signal->n_connections == 0)
+    delete_signal (registry, signal);
+}
+
+static DBusHandlerResult
+handle_method_call_and_unlock (DBusObjectRegistry *registry,
+                               DBusMessage        *message)
+{
+  DBusInterfaceEntry *iface_entry;
+  DBusObjectEntry *object_entry;
+  DBusObjectInfo info;
+  const DBusObjectVTable *vtable;
+  
+  _dbus_assert (registry != NULL);
+  _dbus_assert (message != NULL);  
+
+  /* FIXME handle calls to an object ID instead of just an
+   * interface name
+   */
+  
+  /* If the message isn't to a specific object ID, we send
+   * it to the first object that supports the given interface.
+   */
+  iface_entry = lookup_interface (registry,
+                                  dbus_message_get_name (message),
+                                  FALSE);
+  
+  if (iface_entry == NULL)
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (registry->connection)
+#endif
+        _dbus_connection_unlock (registry->connection);
+
+      return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+    }
+  
+  _dbus_assert (iface_entry->n_objects > 0);
+  _dbus_assert (iface_entry->objects != NULL);
+
+  object_entry = &registry->entries[iface_entry->objects[0]];
+
+
+  /* Once we have an object entry, pass message to the object */
+  
+  _dbus_assert (object_entry->vtable != NULL);
+
+  info_from_entry (registry, &info, object_entry);
+  vtable = object_entry->vtable;
+  
+  /* Drop lock and invoke application code */
+#ifdef DBUS_BUILD_TESTS
+  if (registry->connection)
+#endif
+    _dbus_connection_unlock (registry->connection);
+  
+  (* vtable->message) (&info, message);
+
+  return DBUS_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+typedef struct
+{
+  DBusObjectID id;
+} ObjectEmitData;
+
+static DBusHandlerResult
+handle_signal_and_unlock (DBusObjectRegistry *registry,
+                          DBusMessage        *message)
+{
+  DBusSignalEntry *signal_entry;
+  int i;
+  ObjectEmitData *objects;
+  int n_objects;
+  
+  _dbus_assert (registry != NULL);
+  _dbus_assert (message != NULL);
+
+  signal_entry = lookup_signal (registry,
+                                dbus_message_get_name (message),
+                                FALSE);
+  
+  if (signal_entry == NULL)
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (registry->connection)
+#endif
+        _dbus_connection_unlock (registry->connection);
+      
+      return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+    }
+  
+  _dbus_assert (signal_entry->n_connections > 0);
+  _dbus_assert (signal_entry->connections != NULL);
+
+  /* make a copy for safety vs. reentrancy */
+
+  /* FIXME (?) if you disconnect a signal during (vs. before)
+   * emission, you still receive that signal. To fix this uses more
+   * memory because we don't have a per-connection object at the
+   * moment. You would have to introduce a connection object and
+   * refcount it and have a "disconnected" flag. This is more like
+   * GObject semantics but also maybe not important at this level (the
+   * GObject/Qt wrappers can mop it up).
+   */
+  
+  n_objects = signal_entry->n_connections;
+  objects = dbus_new (ObjectEmitData, n_objects);
+
+  if (objects == NULL)
+    {
+#ifdef DBUS_BUILD_TESTS
+      if (registry->connection)
+#endif
+        _dbus_connection_unlock (registry->connection);
+      
+      return DBUS_HANDLER_RESULT_NEED_MEMORY;
+    }
+
+  i = 0;
+  while (i < signal_entry->n_connections)
+    {
+      DBusObjectEntry *object_entry;
+      int idx;
+      
+      idx = signal_entry->connections[i];
+
+      object_entry = &registry->entries[idx];
+
+      _dbus_assert (object_entry->vtable != NULL);
+      
+      id_from_entry (registry,
+                     &objects[i].id,
+                     object_entry);
+      
+      ++i;
+    }
+
+#ifdef DBUS_BUILD_TESTS
+  if (registry->connection)
+#endif
+    _dbus_connection_ref_unlocked (registry->connection);
+  _dbus_object_registry_ref (registry);
+  dbus_message_ref (message);
+  
+  i = 0;
+  while (i < n_objects)
+    {
+      DBusObjectEntry *object_entry;
+
+      /* If an object ID no longer exists, don't send the
+       * signal
+       */
+      object_entry = validate_id (registry, &objects[i].id);
+      if (object_entry != NULL)
+        {
+          DBusObjectVTable *vtable;
+          DBusObjectInfo info;
+
+          info_from_entry (registry, &info, object_entry);
+          vtable = object_entry->vtable;
+
+          /* Drop lock and invoke application code */
+#ifdef DBUS_BUILD_TESTS
+          if (registry->connection)
+#endif
+            _dbus_connection_unlock (registry->connection);
+          
+          (* vtable->message) (&info, message);
+
+          /* Reacquire lock */
+#ifdef DBUS_BUILD_TESTS
+          if (registry->connection)
+#endif
+            _dbus_connection_lock (registry->connection);
+        }
+      ++i;
+    }
+
+  dbus_message_unref (message);
+  _dbus_object_registry_unref (registry);
+#ifdef DBUS_BUILD_TESTS
+  if (registry->connection)
+#endif
+    _dbus_connection_unref_unlocked (registry->connection);
+
+  dbus_free (objects);
+  
+  /* Drop lock a final time */
+#ifdef DBUS_BUILD_TESTS
+  if (registry->connection)
+#endif
+    _dbus_connection_unlock (registry->connection);
+
+  return DBUS_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+/**
+ * Handle a message, passing it to any objects in the registry that
+ * should receive it.
+ *
+ * @todo handle messages to an object ID, not just those to
+ * an interface name.
+ * 
+ * @param registry the object registry
+ * @param message the message to handle
+ * @returns what to do with the message next
+ */
+DBusHandlerResult
+_dbus_object_registry_handle_and_unlock (DBusObjectRegistry *registry,
+                                         DBusMessage        *message)
+{
+  int type;
+  
+  _dbus_assert (registry != NULL);
+  _dbus_assert (message != NULL);
+  
+  type = dbus_message_get_type (message);
+
+  switch (type)
+    {
+    case DBUS_MESSAGE_TYPE_METHOD_CALL:
+      return handle_method_call_and_unlock (registry, message);
+    case DBUS_MESSAGE_TYPE_SIGNAL:
+      return handle_signal_and_unlock (registry, message);
+    default:
+#ifdef DBUS_BUILD_TESTS
+      if (registry->connection)
+#endif
+        _dbus_connection_unlock (registry->connection);
+
+      return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+    }
+}
+
 dbus_bool_t
 _dbus_object_registry_add_and_unlock (DBusObjectRegistry      *registry,
                                       const char             **interfaces,
@@ -583,6 +1114,7 @@ _dbus_object_registry_remove_and_unlock (DBusObjectRegistry *registry,
       return;
     }
 
+  object_remove_from_signals (registry, entry);
   object_remove_from_interfaces (registry, entry);
   
   info_from_entry (registry, &info, entry);
@@ -600,65 +1132,6 @@ _dbus_object_registry_remove_and_unlock (DBusObjectRegistry *registry,
   (* vtable->unregistered) (&info);
 }
 
-/**
- * Handle a message, passing it to any objects in the registry that
- * should receive it.
- *
- * @todo handle messages to an object ID, not just those to
- * an interface name.
- * 
- * @param registry the object registry
- * @param message the message to handle
- * @returns what to do with the message next
- */
-DBusHandlerResult
-_dbus_object_registry_handle_and_unlock (DBusObjectRegistry *registry,
-                                         DBusMessage        *message)
-{
-  DBusInterfaceEntry *iface_entry;
-  DBusObjectEntry *object_entry;
-  DBusObjectInfo info;
-  const DBusObjectVTable *vtable;
-
-  _dbus_assert (registry != NULL);
-  _dbus_assert (message != NULL);
-  
-  if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
-    return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-  
-  /* If the message isn't to a specific object ID, we send
-   * it to the first object that supports the given interface.
-   */
-  iface_entry = lookup_interface (registry,
-                                  dbus_message_get_name (message),
-                                  FALSE);
-  
-  if (iface_entry == NULL)
-    return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-  
-  _dbus_assert (iface_entry->n_objects > 0);
-  _dbus_assert (iface_entry->objects != NULL);
-
-  object_entry = &registry->entries[iface_entry->objects[0]];
-
-
-  /* Once we have an object entry, pass message to the object */
-  
-  _dbus_assert (object_entry->vtable != NULL);
-
-  info_from_entry (registry, &info, object_entry);
-  vtable = object_entry->vtable;
-  
-  /* Drop lock and invoke application code */
-#ifdef DBUS_BUILD_TESTS
-  if (registry->connection)
-#endif
-    _dbus_connection_unlock (registry->connection);
-  
-  (* vtable->message) (&info, message);
-  
-  return DBUS_HANDLER_RESULT_REMOVE_MESSAGE;
-}
 
 void
 _dbus_object_registry_free_all_unlocked (DBusObjectRegistry *registry)
index 57009c8..bcbd0f8 100644 (file)
@@ -43,8 +43,12 @@ void              _dbus_object_registry_remove_and_unlock (DBusObjectRegistry
 DBusHandlerResult _dbus_object_registry_handle_and_unlock (DBusObjectRegistry      *registry,
                                                            DBusMessage             *message);
 void              _dbus_object_registry_free_all_unlocked (DBusObjectRegistry      *registry);
-
-
+dbus_bool_t       _dbus_object_registry_connect_locked    (DBusObjectRegistry      *registry,
+                                                           const DBusObjectID      *object_id,
+                                                           const char              *signal);
+void              _dbus_object_registry_disconnect_locked (DBusObjectRegistry      *registry,
+                                                           const DBusObjectID      *object_id,
+                                                           const char              *signal);
 
 DBUS_END_DECLS;
 
index a0a53eb..76d0e6f 100644 (file)
@@ -41,8 +41,9 @@ typedef struct DBusCallbackObject DBusCallbackObject;
 
 typedef enum
 {
-  DBUS_HANDLER_RESULT_REMOVE_MESSAGE,     /**< Remove this message, no further processing. */
-  DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS /**< Run any additional handlers that are interested in this message. */
+  DBUS_HANDLER_RESULT_REMOVE_MESSAGE,      /**< Remove this message, no further processing. */
+  DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS, /**< Run any additional handlers that are interested in this message. */
+  DBUS_HANDLER_RESULT_NEED_MEMORY          /**< Need more memory to handle this message. */
 } DBusHandlerResult;
 
 struct DBusObjectInfo