summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-08-07 02:18:54 +0000
committerHavoc Pennington <hp@redhat.com>2003-08-07 02:18:54 +0000
commitcefb84edc5f84011c5a171e5d052e37c56c55d27 (patch)
tree1857fc269db9094f012f819e20d02adcd267c059
parentff8908f1e98eda82b0a77abb449ecff36abf14aa (diff)
downloaddbus-cefb84edc5f84011c5a171e5d052e37c56c55d27.tar.gz
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
-rw-r--r--ChangeLog9
-rw-r--r--dbus/dbus-connection-internal.h1
-rw-r--r--dbus/dbus-connection.c48
-rw-r--r--dbus/dbus-internals.c27
-rw-r--r--dbus/dbus-internals.h2
-rw-r--r--dbus/dbus-object-registry.c617
-rw-r--r--dbus/dbus-object-registry.h8
-rw-r--r--dbus/dbus-object.h5
8 files changed, 637 insertions, 80 deletions
diff --git a/ChangeLog b/ChangeLog
index a5051388..850906cf 100644
--- 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)
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 5bcbcc2f..423df5f8 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -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,
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index ced50bd1..407b4d24 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -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);
diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c
index 6e7f9e16..ccc11776 100644
--- a/dbus/dbus-internals.c
+++ b/dbus/dbus-internals.c
@@ -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.
diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h
index 7acda71a..6d120f1b 100644
--- a/dbus/dbus-internals.h
+++ b/dbus/dbus-internals.h
@@ -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);
diff --git a/dbus/dbus-object-registry.c b/dbus/dbus-object-registry.c
index a4d92216..55f8f749 100644
--- a/dbus/dbus-object-registry.c
+++ b/dbus/dbus-object-registry.c
@@ -35,11 +35,16 @@
* 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)
diff --git a/dbus/dbus-object-registry.h b/dbus/dbus-object-registry.h
index 57009c87..bcbd0f84 100644
--- a/dbus/dbus-object-registry.h
+++ b/dbus/dbus-object-registry.h
@@ -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;
diff --git a/dbus/dbus-object.h b/dbus/dbus-object.h
index a0a53eb0..76d0e6f6 100644
--- a/dbus/dbus-object.h
+++ b/dbus/dbus-object.h
@@ -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