summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-04-11 00:03:06 +0000
committerHavoc Pennington <hp@redhat.com>2003-04-11 00:03:06 +0000
commit6be547d32f018c23ba56426a0bccd08baa2cf440 (patch)
tree00edf1d2628538d66d3f5db7a998c18ffc30ce04
parent7074a2469521949c706f3b9d789d7c8eb9f3ac85 (diff)
2003-04-10 Havoc Pennington <hp@redhat.com>
* dbus/dbus-connection.c (dbus_connection_flush): don't spin on the connection if it's disconnected * bus/activation.c (bus_activation_service_created): use new transaction features to roll back removal of pending activation if we don't successfully create the service after all. Don't remove pending activation if the function fails. * dbus/dbus-list.c (_dbus_list_insert_before_link) (_dbus_list_insert_after_link): new code to facilitate services.c fixes * dbus/dbus-hash.c (_dbus_hash_table_insert_string_preallocated): new functionality, so we can preallocate the ability to insert into a hash table. * bus/connection.c (bus_transaction_add_cancel_hook): new function allowing us to put custom hooks in a transaction to be used for cancelling said transaction * doc/dbus-specification.sgml: add some discussion of secondary service owners, and disallow zero-length service names * bus/services.c (bus_registry_acquire_service): new function, splits out part of bus_driver_handle_acquire_service() and fixes a bug where we didn't remove the service doing the acquiring from the secondary queue if we failed to remove the current owner from the front of the queue.
-rw-r--r--ChangeLog31
-rw-r--r--bus/activation.c106
-rw-r--r--bus/connection.c113
-rw-r--r--bus/connection.h42
-rw-r--r--bus/dispatch.c98
-rw-r--r--bus/driver.c87
-rw-r--r--bus/services.c414
-rw-r--r--bus/services.h71
-rw-r--r--dbus/dbus-connection.c5
-rw-r--r--dbus/dbus-hash.c256
-rw-r--r--dbus/dbus-hash.h11
-rw-r--r--dbus/dbus-list.c36
-rw-r--r--dbus/dbus-list.h76
-rw-r--r--dbus/dbus-spawn.c6
-rw-r--r--doc/dbus-specification.sgml40
-rw-r--r--test/test-service.c6
16 files changed, 1070 insertions, 328 deletions
diff --git a/ChangeLog b/ChangeLog
index fb8dbbfb..f36309a0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2003-04-10 Havoc Pennington <hp@redhat.com>
+
+ * dbus/dbus-connection.c (dbus_connection_flush): don't spin on
+ the connection if it's disconnected
+
+ * bus/activation.c (bus_activation_service_created): use new
+ transaction features to roll back removal of pending activation if
+ we don't successfully create the service after all. Don't remove
+ pending activation if the function fails.
+
+ * dbus/dbus-list.c (_dbus_list_insert_before_link)
+ (_dbus_list_insert_after_link): new code to facilitate
+ services.c fixes
+
+ * dbus/dbus-hash.c (_dbus_hash_table_insert_string_preallocated):
+ new functionality, so we can preallocate the ability to insert
+ into a hash table.
+
+ * bus/connection.c (bus_transaction_add_cancel_hook): new function
+ allowing us to put custom hooks in a transaction to be used for
+ cancelling said transaction
+
+ * doc/dbus-specification.sgml: add some discussion of secondary
+ service owners, and disallow zero-length service names
+
+ * bus/services.c (bus_registry_acquire_service): new function,
+ splits out part of bus_driver_handle_acquire_service() and fixes
+ a bug where we didn't remove the service doing the acquiring
+ from the secondary queue if we failed to remove the current owner
+ from the front of the queue.
+
2003-04-10 Alexander Larsson <alexl@redhat.com>
* doc/dbus-specification.sgml:
diff --git a/bus/activation.c b/bus/activation.c
index 13c147ea..64e0d914 100644
--- a/bus/activation.c
+++ b/bus/activation.c
@@ -63,6 +63,7 @@ struct BusPendingActivationEntry
typedef struct
{
+ int refcount;
BusActivation *activation;
char *service_name;
DBusList *entries;
@@ -94,13 +95,26 @@ handle_timeout_callback (DBusTimeout *timeout,
}
static void
-bus_pending_activation_free (BusPendingActivation *pending_activation)
+bus_pending_activation_ref (BusPendingActivation *pending_activation)
+{
+ _dbus_assert (pending_activation->refcount > 0);
+ pending_activation->refcount += 1;
+}
+
+static void
+bus_pending_activation_unref (BusPendingActivation *pending_activation)
{
DBusList *link;
if (pending_activation == NULL) /* hash table requires this */
return;
+ _dbus_assert (pending_activation->refcount > 0);
+ pending_activation->refcount -= 1;
+
+ if (pending_activation->refcount > 0)
+ return;
+
if (pending_activation->timeout_added)
{
_dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
@@ -396,7 +410,7 @@ bus_activation_new (BusContext *context,
}
activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
- (DBusFreeFunction)bus_pending_activation_free);
+ (DBusFreeFunction)bus_pending_activation_unref);
if (activation->pending_activations == NULL)
{
@@ -466,6 +480,75 @@ child_setup (void *data)
}
}
+typedef struct
+{
+ BusPendingActivation *pending_activation;
+ DBusPreallocatedHash *hash_entry;
+} RestorePendingData;
+
+static void
+restore_pending (void *data)
+{
+ RestorePendingData *d = data;
+
+ _dbus_assert (d->pending_activation != NULL);
+ _dbus_assert (d->hash_entry != NULL);
+
+ _dbus_verbose ("Restoring pending activation for service %s, has timeout = %d\n",
+ d->pending_activation->service_name,
+ d->pending_activation->timeout_added);
+
+ _dbus_hash_table_insert_string_preallocated (d->pending_activation->activation->pending_activations,
+ d->hash_entry,
+ d->pending_activation->service_name, d->pending_activation);
+
+ bus_pending_activation_ref (d->pending_activation);
+
+ d->hash_entry = NULL;
+}
+
+static void
+free_pending_restore_data (void *data)
+{
+ RestorePendingData *d = data;
+
+ if (d->hash_entry)
+ _dbus_hash_table_free_preallocated_entry (d->pending_activation->activation->pending_activations,
+ d->hash_entry);
+
+ bus_pending_activation_unref (d->pending_activation);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_restore_pending_to_transaction (BusTransaction *transaction,
+ BusPendingActivation *pending_activation)
+{
+ RestorePendingData *d;
+
+ d = dbus_new (RestorePendingData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->pending_activation = pending_activation;
+ d->hash_entry = _dbus_hash_table_preallocate_entry (d->pending_activation->activation->pending_activations);
+
+ bus_pending_activation_ref (d->pending_activation);
+
+ if (d->hash_entry == NULL ||
+ !bus_transaction_add_cancel_hook (transaction, restore_pending, d,
+ free_pending_restore_data))
+ {
+ free_pending_restore_data (d);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Saved pending activation to be restored if the transaction fails\n");
+
+ return TRUE;
+}
+
dbus_bool_t
bus_activation_service_created (BusActivation *activation,
const char *service_name,
@@ -521,13 +604,19 @@ bus_activation_service_created (BusActivation *activation,
link = next;
}
+
+ if (!add_restore_pending_to_transaction (transaction, pending_activation))
+ {
+ _dbus_verbose ("Could not add cancel hook to transaction to revert removing pending activation\n");
+ BUS_SET_OOM (error);
+ goto error;
+ }
_dbus_hash_table_remove_string (activation->pending_activations, service_name);
return TRUE;
error:
- _dbus_hash_table_remove_string (activation->pending_activations, service_name);
return FALSE;
}
@@ -785,12 +874,13 @@ bus_activation_activate_service (BusActivation *activation,
}
pending_activation->activation = activation;
+ pending_activation->refcount = 1;
pending_activation->service_name = _dbus_strdup (service_name);
if (!pending_activation->service_name)
{
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
@@ -803,7 +893,7 @@ bus_activation_activate_service (BusActivation *activation,
if (!pending_activation->timeout)
{
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
@@ -815,7 +905,7 @@ bus_activation_activate_service (BusActivation *activation,
NULL))
{
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
@@ -825,7 +915,7 @@ bus_activation_activate_service (BusActivation *activation,
if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
{
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
@@ -834,7 +924,7 @@ bus_activation_activate_service (BusActivation *activation,
pending_activation->service_name, pending_activation))
{
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
return FALSE;
}
}
diff --git a/bus/connection.c b/bus/connection.c
index 80a9ae7a..2cfbeb27 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -139,6 +139,8 @@ bus_connection_disconnected (DBusConnection *connection)
if (!bus_service_remove_owner (service, connection,
transaction, &error))
{
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
@@ -147,7 +149,11 @@ bus_connection_disconnected (DBusConnection *connection)
goto retry;
}
else
- _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
+ {
+ _dbus_verbose ("Failed to remove service owner: %s %s\n",
+ error.name, error.message);
+ _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
+ }
}
bus_transaction_execute_and_free (transaction);
@@ -746,19 +752,31 @@ bus_connection_send_oom_error (DBusConnection *connection,
d->oom_preallocated = NULL;
}
-dbus_bool_t
-bus_connection_add_owned_service (DBusConnection *connection,
- BusService *service)
+void
+bus_connection_add_owned_service_link (DBusConnection *connection,
+ DBusList *link)
{
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
_dbus_assert (d != NULL);
- if (!_dbus_list_append (&d->services_owned,
- service))
+ _dbus_list_append_link (&d->services_owned, link);
+}
+
+dbus_bool_t
+bus_connection_add_owned_service (DBusConnection *connection,
+ BusService *service)
+{
+ DBusList *link;
+
+ link = _dbus_list_alloc_link (service);
+
+ if (link == NULL)
return FALSE;
+ bus_connection_add_owned_service_link (connection, link);
+
return TRUE;
}
@@ -805,6 +823,13 @@ bus_connection_get_name (DBusConnection *connection)
return d->name;
}
+/**
+ * Transactions
+ *
+ * Note that this is fairly fragile; in particular, don't try to use
+ * one transaction across any main loop iterations.
+ */
+
typedef struct
{
BusTransaction *transaction;
@@ -812,10 +837,18 @@ typedef struct
DBusPreallocatedSend *preallocated;
} MessageToSend;
+typedef struct
+{
+ BusTransactionCancelFunction cancel_function;
+ DBusFreeFunction free_data_function;
+ void *data;
+} CancelHook;
+
struct BusTransaction
{
DBusList *connections;
BusContext *context;
+ DBusList *cancel_hooks;
};
static void
@@ -831,6 +864,39 @@ message_to_send_free (DBusConnection *connection,
dbus_free (to_send);
}
+static void
+cancel_hook_cancel (void *element,
+ void *data)
+{
+ CancelHook *ch = element;
+
+ _dbus_verbose ("Running transaction cancel hook\n");
+
+ if (ch->cancel_function)
+ (* ch->cancel_function) (ch->data);
+}
+
+static void
+cancel_hook_free (void *element,
+ void *data)
+{
+ CancelHook *ch = element;
+
+ if (ch->free_data_function)
+ (* ch->free_data_function) (ch->data);
+
+ dbus_free (ch);
+}
+
+static void
+free_cancel_hooks (BusTransaction *transaction)
+{
+ _dbus_list_foreach (&transaction->cancel_hooks,
+ cancel_hook_free, NULL);
+
+ _dbus_list_clear (&transaction->cancel_hooks);
+}
+
BusTransaction*
bus_transaction_new (BusContext *context)
{
@@ -980,6 +1046,11 @@ bus_transaction_cancel_and_free (BusTransaction *transaction)
_dbus_assert (transaction->connections == NULL);
+ _dbus_list_foreach (&transaction->cancel_hooks,
+ cancel_hook_cancel, NULL);
+
+ free_cancel_hooks (transaction);
+
dbus_free (transaction);
}
@@ -1036,6 +1107,8 @@ bus_transaction_execute_and_free (BusTransaction *transaction)
_dbus_assert (transaction->connections == NULL);
+ free_cancel_hooks (transaction);
+
dbus_free (transaction);
}
@@ -1090,3 +1163,31 @@ bus_transaction_send_error_reply (BusTransaction *transaction,
return TRUE;
}
+
+dbus_bool_t
+bus_transaction_add_cancel_hook (BusTransaction *transaction,
+ BusTransactionCancelFunction cancel_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ CancelHook *ch;
+
+ ch = dbus_new (CancelHook, 1);
+ if (ch == NULL)
+ return FALSE;
+
+ ch->cancel_function = cancel_function;
+ ch->data = data;
+ ch->free_data_function = free_data_function;
+
+ /* It's important that the hooks get run in reverse order that they
+ * were added
+ */
+ if (!_dbus_list_prepend (&transaction->cancel_hooks, ch))
+ {
+ dbus_free (ch);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/bus/connection.h b/bus/connection.h
index 0d64e987..6108bdfd 100644
--- a/bus/connection.h
+++ b/bus/connection.h
@@ -25,6 +25,7 @@
#define BUS_CONNECTION_H
#include <dbus/dbus.h>
+#include <dbus/dbus-list.h>
#include "bus.h"
typedef dbus_bool_t (* BusConnectionForeachFunction) (DBusConnection *connection,
@@ -53,10 +54,13 @@ void bus_connection_send_oom_error (DBusConnection *connection,
DBusMessage *in_reply_to);
/* called by services.c */
-dbus_bool_t bus_connection_add_owned_service (DBusConnection *connection,
- BusService *service);
-void bus_connection_remove_owned_service (DBusConnection *connection,
- BusService *service);
+dbus_bool_t bus_connection_add_owned_service (DBusConnection *connection,
+ BusService *service);
+void bus_connection_remove_owned_service (DBusConnection *connection,
+ BusService *service);
+void bus_connection_add_owned_service_link (DBusConnection *connection,
+ DBusList *link);
+
/* called by driver.c */
dbus_bool_t bus_connection_set_name (DBusConnection *connection,
@@ -74,18 +78,24 @@ dbus_bool_t bus_connection_get_groups (DBusConnection *connection,
BusPolicy* bus_connection_get_policy (DBusConnection *connection);
/* transaction API so we can send or not send a block of messages as a whole */
-BusTransaction* bus_transaction_new (BusContext *context);
-BusContext* bus_transaction_get_context (BusTransaction *transaction);
-BusConnections* bus_transaction_get_connections (BusTransaction *transaction);
-dbus_bool_t bus_transaction_send_message (BusTransaction *transaction,
- DBusConnection *connection,
- DBusMessage *message);
-dbus_bool_t bus_transaction_send_error_reply (BusTransaction *transaction,
- DBusConnection *connection,
- const DBusError *error,
- DBusMessage *in_reply_to);
-void bus_transaction_cancel_and_free (BusTransaction *transaction);
-void bus_transaction_execute_and_free (BusTransaction *transaction);
+typedef void (* BusTransactionCancelFunction) (void *data);
+
+BusTransaction* bus_transaction_new (BusContext *context);
+BusContext* bus_transaction_get_context (BusTransaction *transaction);
+BusConnections* bus_transaction_get_connections (BusTransaction *transaction);
+dbus_bool_t bus_transaction_send_message (BusTransaction *transaction,
+ DBusConnection *connection,
+ DBusMessage *message);
+dbus_bool_t bus_transaction_send_error_reply (BusTransaction *transaction,
+ DBusConnection *connection,
+ const DBusError *error,
+ DBusMessage *in_reply_to);
+void bus_transaction_cancel_and_free (BusTransaction *transaction);
+void bus_transaction_execute_and_free (BusTransaction *transaction);
+dbus_bool_t bus_transaction_add_cancel_hook (BusTransaction *transaction,
+ BusTransactionCancelFunction cancel_function,
+ void *data,
+ DBusFreeFunction free_data_function);
#endif /* BUS_CONNECTION_H */
diff --git a/bus/dispatch.c b/bus/dispatch.c
index e867674b..f6ddc76a 100644
--- a/bus/dispatch.c
+++ b/bus/dispatch.c
@@ -1442,6 +1442,47 @@ check_send_exit_to_service (BusContext *context,
return retval;
}
+static dbus_bool_t
+check_got_error (BusContext *context,
+ DBusConnection *connection,
+ const char *error_name)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not get an expected error\n");
+ goto out;
+ }
+
+ if (!dbus_message_get_is_error (message))
+ {
+ _dbus_warn ("Expected an error, got %s\n",
+ dbus_message_get_name (message));
+ goto out;
+ }
+
+ if (!dbus_message_name_is (message, error_name))
+ {
+ _dbus_warn ("Expected error %s, got %s instead\n",
+ error_name,
+ dbus_message_get_name (message));
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
#define EXISTENT_SERVICE_NAME "org.freedesktop.DBus.TestSuiteEchoService"
/* returns TRUE if the correct thing happens,
@@ -1551,6 +1592,7 @@ check_existent_service_activation (BusContext *context,
else
{
dbus_bool_t got_service_deleted;
+ dbus_bool_t got_error;
if (!check_base_service_activated (context, connection,
message, &base_service))
@@ -1570,9 +1612,22 @@ check_existent_service_activation (BusContext *context,
}
got_service_deleted = dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_DELETED);
-
+ got_error = dbus_message_get_is_error (message);
+
dbus_connection_return_message (connection, message);
message = NULL;
+
+ if (got_error)
+ {
+ if (!check_got_error (context, connection,
+ DBUS_ERROR_SPAWN_CHILD_EXITED))
+ goto out;
+
+ /* A service deleted should be coming along now after this error.
+ * We can also get the error *after* the service deleted.
+ */
+ got_service_deleted = TRUE;
+ }
if (got_service_deleted)
{
@@ -1589,34 +1644,19 @@ check_existent_service_activation (BusContext *context,
if (csdd.failed)
goto out;
- /* Now we should get an error about the service exiting */
- block_connection_until_message_from_bus (context, connection);
-
- /* and process everything again */
- bus_test_run_everything (context);
-
- message = pop_message_waiting_for_memory (connection);
- if (message == NULL)
- {
- _dbus_warn ("Did not get an error from the service %s exiting\n",
- EXISTENT_SERVICE_NAME);
- goto out;
- }
-
- if (!dbus_message_get_is_error (message))
- {
- _dbus_warn ("Expected an error due to service exiting, got %s\n",
- dbus_message_get_name (message));
- goto out;
- }
-
- if (!dbus_message_name_is (message,
- DBUS_ERROR_SPAWN_CHILD_EXITED))
+ /* Now we should get an error about the service exiting
+ * if we didn't get it before.
+ */
+ if (!got_error)
{
- _dbus_warn ("Expected error %s on service exit, got %s instead\n",
- DBUS_ERROR_SPAWN_CHILD_EXITED,
- dbus_message_get_name (message));
- goto out;
+ block_connection_until_message_from_bus (context, connection);
+
+ /* and process everything again */
+ bus_test_run_everything (context);
+
+ if (!check_got_error (context, connection,
+ DBUS_ERROR_SPAWN_CHILD_EXITED))
+ goto out;
}
}
else
@@ -1785,7 +1825,7 @@ bus_dispatch_test (const DBusString *test_data_dir)
if (!check_hello_message (context, baz))
_dbus_assert_not_reached ("hello message failed");
-#if 0
+#if 1
check2_try_iterations (context, foo, "existent_service_activation",
check_existent_service_activation);
#endif
diff --git a/bus/driver.c b/bus/driver.c
index bb8ac296..33017e9f 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -438,13 +438,10 @@ bus_driver_handle_acquire_service (DBusConnection *connection,
{
DBusMessage *reply;
DBusString service_name;
- BusService *service;
char *name;
int service_reply;
int flags;
dbus_bool_t retval;
- DBusConnection *old_owner;
- DBusConnection *current_owner;
BusRegistry *registry;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -461,27 +458,14 @@ bus_driver_handle_acquire_service (DBusConnection *connection,
retval = FALSE;
reply = NULL;
-
- if (*name == ':')
- {
- /* Not allowed; only base services can start with ':' */
- dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
- "Cannot acquire a service starting with ':' such as \"%s\"",
- name);
-
- _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"", name);
-
- goto out;
- }
_dbus_string_init_const (&service_name, name);
-
- service = bus_registry_lookup (registry, &service_name);
- if (service != NULL)
- old_owner = bus_service_get_primary_owner (service);
- else
- old_owner = NULL;
+ if (!bus_registry_acquire_service (registry, connection,
+ &service_name, flags,
+ &service_reply, transaction,
+ error))
+ goto out;
reply = dbus_message_new_reply (message);
if (reply == NULL)
@@ -495,67 +479,8 @@ bus_driver_handle_acquire_service (DBusConnection *connection,
BUS_SET_OOM (error);
goto out;
}
-
- if (service == NULL)
- {
- service = bus_registry_ensure (registry,
- &service_name, connection, transaction, error);
- if (service == NULL)
- goto out;
- }
-
- current_owner = bus_service_get_primary_owner (service);
-
- if (old_owner == NULL)
- {
- _dbus_assert (current_owner == connection);
-
- bus_service_set_prohibit_replacement (service,
- (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));
-
- service_reply = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
- }
- else if (old_owner == connection)
- service_reply = DBUS_SERVICE_REPLY_ALREADY_OWNER;
- else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
- service_reply = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
- else if (bus_service_get_prohibit_replacement (service))
- {
- /* Queue the connection */
- if (!bus_service_add_owner (service, connection,
- transaction, error))
- goto out;
-
- service_reply = DBUS_SERVICE_REPLY_IN_QUEUE;
- }
- else
- {
- /* Replace the current owner */
-
- /* We enqueue the new owner and remove the first one because
- * that will cause ServiceAcquired and ServiceLost messages to
- * be sent.
- */
-
- /* FIXME this is broken, if the remove_owner fails
- * we don't undo the add_owner
- * (easiest fix is probably to move all this to
- * services.c and have a single routine for it)
- */
-
- if (!bus_service_add_owner (service, connection,
- transaction, error))
- goto out;
-
- if (!bus_service_remove_owner (service, old_owner,
- transaction, error))
- goto out;
-
- _dbus_assert (connection == bus_service_get_primary_owner (service));
- service_reply = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
- }
- if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32, service_reply, 0))
+ if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32, service_reply, DBUS_TYPE_INVALID))
{
BUS_SET_OOM (error);
goto out;
diff --git a/bus/services.c b/bus/services.c
index 3a7a0756..dfc3ed08 100644
--- a/bus/services.c
+++ b/bus/services.c
@@ -33,6 +33,8 @@
struct BusService
{
+ int refcount;
+
BusRegistry *registry;
char *name;
DBusList *owners;
@@ -142,7 +144,8 @@ bus_registry_ensure (BusRegistry *registry,
}
service->registry = registry;
-
+ service->refcount = 1;
+
if (!_dbus_string_copy_data (service_name, &service->name))
{
_dbus_mem_pool_dealloc (registry->service_pool, service);
@@ -152,24 +155,21 @@ bus_registry_ensure (BusRegistry *registry,
if (!bus_driver_send_service_created (service->name, transaction, error))
{
- dbus_free (service->name);
- _dbus_mem_pool_dealloc (registry->service_pool, service);
+ bus_service_unref (service);
return NULL;
}
if (!bus_activation_service_created (bus_context_get_activation (registry->context),
service->name, transaction, error))
{
- dbus_free (service->name);
- _dbus_mem_pool_dealloc (registry->service_pool, service);
+ bus_service_unref (service);
return NULL;
}
if (!bus_service_add_owner (service, owner_if_created,
transaction, error))
{
- dbus_free (service->name);
- _dbus_mem_pool_dealloc (registry->service_pool, service);
+ bus_service_unref (service);
return NULL;
}
@@ -177,11 +177,7 @@ bus_registry_ensure (BusRegistry *registry,
service->name,
service))
{
- bus_connection_remove_owned_service (owner_if_created,
- service);
- _dbus_list_clear (&service->owners);
- dbus_free (service->name);
- _dbus_mem_pool_dealloc (registry->service_pool, service);
+ /* The add_owner gets reverted on transaction cancel */
BUS_SET_OOM (error);
return NULL;
}
@@ -250,6 +246,209 @@ bus_registry_list_services (BusRegistry *registry,
}
dbus_bool_t
+bus_registry_acquire_service (BusRegistry *registry,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t flags,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ dbus_bool_t retval;
+ DBusConnection *old_owner;
+ DBusConnection *current_owner;
+ BusService *service;
+
+ retval = FALSE;
+
+ if (_dbus_string_get_length (service_name) == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Zero-length service name is not allowed");
+
+ _dbus_verbose ("Attempt to acquire zero-length service name\n");
+
+ goto out;
+ }
+
+ if (_dbus_string_get_byte (service_name, 0) == ':')
+ {
+ /* Not allowed; only base services can start with ':' */
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Cannot acquire a service starting with ':' such as \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ goto out;
+ }
+
+ service = bus_registry_lookup (registry, service_name);
+
+ if (service != NULL)
+ old_owner = bus_service_get_primary_owner (service);
+ else
+ old_owner = NULL;
+
+ if (service == NULL)
+ {
+ service = bus_registry_ensure (registry,
+ service_name, connection, transaction, error);
+ if (service == NULL)
+ goto out;
+ }
+
+ current_owner = bus_service_get_primary_owner (service);
+
+ if (old_owner == NULL)
+ {
+ _dbus_assert (current_owner == connection);
+
+ bus_service_set_prohibit_replacement (service,
+ (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));
+
+ *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
+ }
+ else if (old_owner == connection)
+ *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
+ else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
+ *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
+ else if (bus_service_get_prohibit_replacement (service))
+ {
+ /* Queue the connection */
+ if (!bus_service_add_owner (service, connection,
+ transaction, error))
+ goto out;
+
+ *result = DBUS_SERVICE_REPLY_IN_QUEUE;
+ }
+ else
+ {
+ /* Replace the current owner */
+
+ /* We enqueue the new owner and remove the first one because
+ * that will cause ServiceAcquired and ServiceLost messages to
+ * be sent.
+ */
+
+ if (!bus_service_add_owner (service, connection,
+ transaction, error))
+ goto out;
+
+ if (!bus_service_remove_owner (service, old_owner,
+ transaction, error))
+ goto out;
+
+ _dbus_assert (connection == bus_service_get_primary_owner (service));
+ *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+static void
+bus_service_unlink_owner (BusService *service,
+ DBusConnection *owner)
+{
+ _dbus_list_remove_last (&service->owners, owner);
+ bus_connection_remove_owned_service (owner, service);
+}
+
+static void
+bus_service_unlink (BusService *service)
+{
+ _dbus_assert (service->owners == NULL);
+
+ /* the service may not be in the hash, if
+ * the failure causing transaction cancel
+ * was in the right place, but that's OK
+ */
+ _dbus_hash_table_remove_string (service->registry->service_hash,
+ service->name);
+
+ bus_service_unref (service);
+}
+
+static void
+bus_service_relink (BusService *service,
+ DBusPreallocatedHash *preallocated)
+{
+ _dbus_assert (service->owners == NULL);
+ _dbus_assert (preallocated != NULL);
+
+ _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
+ preallocated,
+ service->name,
+ service);
+
+ bus_service_ref (service);
+}
+
+typedef struct
+{
+ DBusConnection *connection;
+ BusService *service;
+} OwnershipCancelData;
+
+static void
+cancel_ownership (void *data)
+{
+ OwnershipCancelData *d = data;
+
+ /* We don't need to send messages notifying of these
+ * changes, since we're reverting something that was
+ * cancelled (effectively never really happened)
+ */
+ bus_service_unlink_owner (d->service, d->connection);
+
+ if (d->service->owners == NULL)
+ bus_service_unlink (d->service);
+}
+
+static void
+free_ownership_cancel_data (void *data)
+{
+ OwnershipCancelData *d = data;
+
+ dbus_connection_unref (d->connection);
+ bus_service_unref (d->service);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_cancel_ownership_to_transaction (BusTransaction *transaction,
+ BusService *service,
+ DBusConnection *connection)
+{
+ OwnershipCancelData *d;
+
+ d = dbus_new (OwnershipCancelData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->service = service;
+ d->connection = connection;
+
+ if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
+ free_ownership_cancel_data))
+ {
+ dbus_free (d);
+ return FALSE;
+ }
+
+ bus_service_ref (d->service);
+ dbus_connection_ref (d->connection);
+
+ return TRUE;
+}
+
+/* this function is self-cancelling if you cancel the transaction */
+dbus_bool_t
bus_service_add_owner (BusService *service,
DBusConnection *owner,
BusTransaction *transaction,
@@ -279,10 +478,147 @@ bus_service_add_owner (BusService *service,
BUS_SET_OOM (error);
return FALSE;
}
+
+ if (!add_cancel_ownership_to_transaction (transaction,
+ service,
+ owner))
+ {
+ bus_service_unlink_owner (service, owner);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
return TRUE;
}
+typedef struct
+{
+ DBusConnection *connection;
+ BusService *service;
+ DBusConnection *before_connection; /* restore to position before this connection in owners list */
+ DBusList *connection_link;
+ DBusList *service_link;
+ DBusPreallocatedHash *hash_entry;
+} OwnershipRestoreData;
+
+static void
+restore_ownership (void *data)
+{
+ OwnershipRestoreData *d = data;
+ DBusList *link;
+
+ _dbus_assert (d->service_link != NULL);
+ _dbus_assert (d->connection_link != NULL);
+
+ if (d->service->owners == NULL)
+ {
+ _dbus_assert (d->hash_entry != NULL);
+ bus_service_relink (d->service, d->hash_entry);
+ }
+ else
+ {
+ _dbus_assert (d->hash_entry == NULL);
+ }
+
+ /* We don't need to send messages notifying of these
+ * changes, since we're reverting something that was
+ * cancelled (effectively never really happened)
+ */
+ link = _dbus_list_get_first_link (&d->service->owners);
+ while (link != NULL)
+ {
+ if (link->data == d->before_connection)
+ break;
+
+ link = _dbus_list_get_next_link (&d->service->owners, link);
+ }
+
+ _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
+
+ /* Note that removing then restoring this changes the order in which
+ * ServiceDeleted messages are sent on destruction of the
+ * connection. This should be OK as the only guarantee there is
+ * that the base service is destroyed last, and we never even
+ * tentatively remove the base service.
+ */
+ bus_connection_add_owned_service_link (d->connection, d->service_link);
+
+ d->hash_entry = NULL;
+ d->service_link = NULL;
+ d->connection_link = NULL;
+}
+
+static void
+free_ownership_restore_data (void *data)
+{
+ OwnershipRestoreData *d = data;
+
+ if (d->service_link)
+ _dbus_list_free_link (d->service_link);
+ if (d->connection_link)
+ _dbus_list_free_link (d->connection_link);
+ if (d->hash_entry)
+ _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
+ d->hash_entry);
+
+ dbus_connection_unref (d->connection);
+ bus_service_unref (d->service);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_restore_ownership_to_transaction (BusTransaction *transaction,
+ BusService *service,
+ DBusConnection *connection)
+{
+ OwnershipRestoreData *d;
+ DBusList *link;
+
+ d = dbus_new (OwnershipRestoreData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->service = service;
+ d->connection = connection;
+ d->service_link = _dbus_list_alloc_link (service);
+ d->connection_link = _dbus_list_alloc_link (connection);
+ d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
+
+ bus_service_ref (d->service);
+ dbus_connection_ref (d->connection);
+
+ d->before_connection = NULL;
+ link = _dbus_list_get_first_link (&service->owners);
+ while (link != NULL)
+ {
+ if (link->data == connection)
+ {
+ link = _dbus_list_get_next_link (&service->owners, link);
+
+ if (link)
+ d->before_connection = link->data;
+
+ break;
+ }
+
+ link = _dbus_list_get_next_link (&service->owners, link);
+ }
+
+ if (d->service_link == NULL ||
+ d->connection_link == NULL ||
+ d->hash_entry == NULL ||
+ !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
+ free_ownership_restore_data))
+ {
+ free_ownership_restore_data (d);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* this function is self-cancelling if you cancel the transaction */
dbus_bool_t
bus_service_remove_owner (BusService *service,
DBusConnection *owner,
@@ -309,7 +645,6 @@ bus_service_remove_owner (BusService *service,
}
else if (_dbus_list_length_is_one (&service->owners))
{
- /* We are the only owner - send service deleted */
if (!bus_driver_send_service_deleted (service->name,
transaction, error))
return FALSE;
@@ -321,31 +656,52 @@ bus_service_remove_owner (BusService *service,
_dbus_assert (link != NULL);
link = _dbus_list_get_next_link (&service->owners, link);
- if (link != NULL)
- {
- /* This will be our new owner */
- if (!bus_driver_send_service_acquired (link->data,
- service->name,
- transaction,
- error))
- return FALSE;
- }
+ _dbus_assert (link != NULL);
+
+ /* This will be our new owner */
+ if (!bus_driver_send_service_acquired (link->data,
+ service->name,
+ transaction,
+ error))
+ return FALSE;
+ }
+
+ if (!add_restore_ownership_to_transaction (transaction, service, owner))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
}
- _dbus_list_remove_last (&service->owners, owner);
- bus_connection_remove_owned_service (owner, service);
+ bus_service_unlink_owner (service, owner);
if (service->owners == NULL)
+ bus_service_unlink (service);
+
+ return TRUE;
+}
+
+void
+bus_service_ref (BusService *service)
+{
+ _dbus_assert (service->refcount > 0);
+
+ service->refcount += 1;
+}
+
+void
+bus_service_unref (BusService *service)
+{
+ _dbus_assert (service->refcount > 0);
+
+ service->refcount -= 1;
+
+ if (service->refcount == 0)
{
- /* Delete service (already sent message that it was deleted above) */
- _dbus_hash_table_remove_string (service->registry->service_hash,
- service->name);
+ _dbus_assert (service->owners == NULL);
dbus_free (service->name);
_dbus_mem_pool_dealloc (service->registry->service_pool, service);
}
-
- return TRUE;
}
DBusConnection*
diff --git a/bus/services.h b/bus/services.h
index aba2989a..bed950c3 100644
--- a/bus/services.h
+++ b/bus/services.h
@@ -32,38 +32,47 @@
typedef void (* BusServiceForeachFunction) (BusService *service,
void *data);
-BusRegistry* bus_registry_new (BusContext *context);
-void bus_registry_ref (BusRegistry *registry);
-void bus_registry_unref (BusRegistry *registry);
-BusService* bus_registry_lookup (BusRegistry *registry,
- const DBusString *service_name);
-BusService* bus_registry_ensure (BusRegistry *registry,
- const DBusString *service_name,
- DBusConnection *owner_if_created,
- BusTransaction *transaction,
- DBusError *error);
-void bus_registry_foreach (BusRegistry *registry,
- BusServiceForeachFunction function,
- void *data);
-dbus_bool_t bus_registry_list_services (BusRegistry *registry,
- char ***listp,
- int *array_len);
+BusRegistry* bus_registry_new (BusContext *context);
+void bus_registry_ref (BusRegistry *registry);
+void bus_registry_unref (BusRegistry *registry);
+BusService* bus_registry_lookup (BusRegistry *registry,
+ const DBusString *service_name);
+BusService* bus_registry_ensure (BusRegistry *registry,
+ const DBusString *service_name,
+ DBusConnection *owner_if_created,
+ BusTransaction *transaction,
+ DBusError *error);
+void bus_registry_foreach (BusRegistry *registry,
+ BusServiceForeachFunction function,
+ void *data);
+dbus_bool_t bus_registry_list_services (BusRegistry *registry,
+ char ***listp,
+ int *array_len);
+dbus_bool_t bus_registry_acquire_service (BusRegistry *registry,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t flags,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error);
+void bus_service_ref (BusService *service);
+void bus_service_unref (BusService *service);
+dbus_bool_t bus_service_add_owner (BusService *service,
+ DBusConnection *owner,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_service_remove_owner (BusService *service,
+ DBusConnection *owner,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_service_has_owner (BusService *service,
+ DBusConnection *owner);
+DBusConnection* bus_service_get_primary_owner (BusService *service);
+void bus_service_set_prohibit_replacement (BusService *service,
+ dbus_bool_t prohibit_replacement);
+dbus_bool_t bus_service_get_prohibit_replacement (BusService *service);
+const char* bus_service_get_name (BusService *service);
-dbus_bool_t bus_service_add_owner (BusService *service,
- DBusConnection *owner,
- BusTransaction *transaction,
- DBusError *error);
-dbus_bool_t bus_service_remove_owner (BusService *service,
- DBusConnection *owner,
- BusTransaction *transaction,
- DBusError *error);
-dbus_bool_t bus_service_has_owner (BusService *service,
- DBusConnection *owner);
-DBusConnection* bus_service_get_primary_owner (BusService *service);
-void bus_service_set_prohibit_replacement (BusService *service,
- dbus_bool_t prohibit_replacement);
-dbus_bool_t bus_service_get_prohibit_replacement (BusService *service);
-const char* bus_service_get_name (BusService *service);
#endif /* BUS_SERVICES_H */
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index 0961e49d..5a6277de 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -558,7 +558,7 @@ _dbus_connection_do_iteration (DBusConnection *connection,
flags &= ~DBUS_ITERATION_DO_WRITING;
if (_dbus_connection_acquire_io_path (connection,
- (flags & DBUS_ITERATION_BLOCK)?timeout_milliseconds:0))
+ (flags & DBUS_ITERATION_BLOCK) ? timeout_milliseconds : 0))
{
_dbus_transport_do_iteration (connection->transport,
flags, timeout_milliseconds);
@@ -1596,7 +1596,8 @@ dbus_connection_flush (DBusConnection *connection)
DBusDispatchStatus status;
dbus_mutex_lock (connection->mutex);
- while (connection->n_outgoing > 0)
+ while (connection->n_outgoing > 0 &&
+ dbus_connection_get_is_connected (connection))
_dbus_connection_do_iteration (connection,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_DO_WRITING |
diff --git a/dbus/dbus-hash.c b/dbus/dbus-hash.c
index ff3f3b08..8d2747b4 100644
--- a/dbus/dbus-hash.c
+++ b/dbus/dbus-hash.c
@@ -152,10 +152,11 @@ struct DBusHashEntry
/**
* Function used to find and optionally create a hash entry.
*/
-typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket);
+typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
/**
* @brief Internals of DBusHashTable.
@@ -220,24 +221,27 @@ typedef struct
int n_entries_on_init; /**< used to detect table resize since initialization */
} DBusRealHashIter;
-static DBusHashEntry* find_direct_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket);
-static DBusHashEntry* find_string_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket);
-static unsigned int string_hash (const char *str);
-static void rebuild_table (DBusHashTable *table);
-static DBusHashEntry* alloc_entry (DBusHashTable *table);
-static void remove_entry (DBusHashTable *table,
- DBusHashEntry **bucket,
- DBusHashEntry *entry);
-static void free_entry (DBusHashTable *table,
- DBusHashEntry *entry);
-static void free_entry_data (DBusHashTable *table,
- DBusHashEntry *entry);
+static DBusHashEntry* find_direct_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static DBusHashEntry* find_string_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static unsigned int string_hash (const char *str);
+static void rebuild_table (DBusHashTable *table);
+static DBusHashEntry* alloc_entry (DBusHashTable *table);
+static void remove_entry (DBusHashTable *table,
+ DBusHashEntry **bucket,
+ DBusHashEntry *entry);
+static void free_entry (DBusHashTable *table,
+ DBusHashEntry *entry);
+static void free_entry_data (DBusHashTable *table,
+ DBusHashEntry *entry);
+
/** @} */
@@ -725,7 +729,7 @@ _dbus_hash_iter_lookup (DBusHashTable *table,
real = (DBusRealHashIter*) iter;
- entry = (* table->find_function) (table, key, create_if_not_found, &bucket);
+ entry = (* table->find_function) (table, key, create_if_not_found, &bucket, NULL);
if (entry == NULL)
return FALSE;
@@ -742,22 +746,14 @@ _dbus_hash_iter_lookup (DBusHashTable *table,
return TRUE;
}
-static DBusHashEntry*
-add_entry (DBusHashTable *table,
- unsigned int idx,
- void *key,
- DBusHashEntry ***bucket)
+static void
+add_allocated_entry (DBusHashTable *table,
+ DBusHashEntry *entry,
+ unsigned int idx,
+ void *key,
+ DBusHashEntry ***bucket)
{
- DBusHashEntry *entry;
- DBusHashEntry **b;
-
- entry = alloc_entry (table);
- if (entry == NULL)
- {
- if (bucket)
- *bucket = NULL;
- return NULL;
- }
+ DBusHashEntry **b;
entry->key = key;
@@ -776,10 +772,37 @@ add_entry (DBusHashTable *table,
if (table->n_entries >= table->hi_rebuild_size ||
table->n_entries < table->lo_rebuild_size)
rebuild_table (table);
+}
+
+static DBusHashEntry*
+add_entry (DBusHashTable *table,
+ unsigned int idx,
+ void *key,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ DBusHashEntry *entry;
+
+ if (preallocated == NULL)
+ {
+ entry = alloc_entry (table);
+ if (entry == NULL)
+ {
+ if (bucket)
+ *bucket = NULL;
+ return NULL;
+ }
+ }
+ else
+ {
+ entry = (DBusHashEntry*) preallocated;
+ }
+
+ add_allocated_entry (table, entry, idx, key, bucket);
return entry;
}
-
+
static unsigned int
string_hash (const char *str)
{
@@ -802,6 +825,8 @@ string_hash (const char *str)
* works well both for decimal and non-decimal strings.
*/
+ /* FIXME the hash function in GLib is better than this one */
+
result = 0;
while (TRUE)
{
@@ -817,10 +842,11 @@ string_hash (const char *str)
}
static DBusHashEntry*
-find_string_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket)
+find_string_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
{
DBusHashEntry *entry;
unsigned int idx;
@@ -838,6 +864,10 @@ find_string_function (DBusHashTable *table,
{
if (bucket)
*bucket = &(table->buckets[idx]);
+
+ if (preallocated)
+ _dbus_hash_table_free_preallocated_entry (table, preallocated);
+
return entry;
}
@@ -845,16 +875,19 @@ find_string_function (DBusHashTable *table,
}
if (create_if_not_found)
- entry = add_entry (table, idx, key, bucket);
+ entry = add_entry (table, idx, key, bucket, preallocated);
+ else if (preallocated)
+ _dbus_hash_table_free_preallocated_entry (table, preallocated);
return entry;
}
static DBusHashEntry*
-find_direct_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket)
+find_direct_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
{
DBusHashEntry *entry;
unsigned int idx;
@@ -872,6 +905,10 @@ find_direct_function (DBusHashTable *table,
{
if (bucket)
*bucket = &(table->buckets[idx]);
+
+ if (preallocated)
+ _dbus_hash_table_free_preallocated_entry (table, preallocated);
+
return entry;
}
@@ -880,7 +917,9 @@ find_direct_function (DBusHashTable *table,
/* Entry not found. Add a new one to the bucket. */
if (create_if_not_found)
- entry = add_entry (table, idx, key, bucket);
+ entry = add_entry (table, idx, key, bucket, preallocated);
+ else if (preallocated)
+ _dbus_hash_table_free_preallocated_entry (table, preallocated);
return entry;
}
@@ -1022,7 +1061,7 @@ _dbus_hash_table_lookup_string (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_STRING);
- entry = (* table->find_function) (table, (char*) key, FALSE, NULL);
+ entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL);
if (entry)
return entry->value;
@@ -1047,7 +1086,7 @@ _dbus_hash_table_lookup_int (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_INT);
- entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL);
+ entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL, NULL);
if (entry)
return entry->value;
@@ -1072,7 +1111,7 @@ _dbus_hash_table_lookup_pointer (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_POINTER);
- entry = (* table->find_function) (table, key, FALSE, NULL);
+ entry = (* table->find_function) (table, key, FALSE, NULL, NULL);
if (entry)
return entry->value;
@@ -1097,7 +1136,7 @@ _dbus_hash_table_lookup_ulong (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_ULONG);
- entry = (* table->find_function) (table, (void*) key, FALSE, NULL);
+ entry = (* table->find_function) (table, (void*) key, FALSE, NULL, NULL);
if (entry)
return entry->value;
@@ -1122,7 +1161,7 @@ _dbus_hash_table_remove_string (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_STRING);
- entry = (* table->find_function) (table, (char*) key, FALSE, &bucket);
+ entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL);
if (entry)
{
@@ -1150,7 +1189,7 @@ _dbus_hash_table_remove_int (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_INT);
- entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket);
+ entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket, NULL);
if (entry)
{
@@ -1178,7 +1217,7 @@ _dbus_hash_table_remove_pointer (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_POINTER);
- entry = (* table->find_function) (table, key, FALSE, &bucket);
+ entry = (* table->find_function) (table, key, FALSE, &bucket, NULL);
if (entry)
{
@@ -1207,7 +1246,7 @@ _dbus_hash_table_remove_ulong (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_ULONG);
- entry = (* table->find_function) (table, (void*) key, FALSE, &bucket);
+ entry = (* table->find_function) (table, (void*) key, FALSE, &bucket, NULL);
if (entry)
{
@@ -1238,24 +1277,17 @@ _dbus_hash_table_insert_string (DBusHashTable *table,
char *key,
void *value)
{
- DBusHashEntry *entry;
+ DBusPreallocatedHash *preallocated;
_dbus_assert (table->key_type == DBUS_HASH_STRING);
-
- entry = (* table->find_function) (table, key, TRUE, NULL);
-
- if (entry == NULL)
- return FALSE; /* no memory */
-
- if (table->free_key_function && entry->key != key)
- (* table->free_key_function) (entry->key);
- if (table->free_value_function && entry->value != value)
- (* table->free_value_function) (entry->value);
-
- entry->key = key;
- entry->value = value;
+ preallocated = _dbus_hash_table_preallocate_entry (table);
+ if (preallocated == NULL)
+ return FALSE;
+ _dbus_hash_table_insert_string_preallocated (table, preallocated,
+ key, value);
+
return TRUE;
}
@@ -1283,7 +1315,7 @@ _dbus_hash_table_insert_int (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_INT);
- entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL);
+ entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL, NULL);
if (entry == NULL)
return FALSE; /* no memory */
@@ -1324,7 +1356,7 @@ _dbus_hash_table_insert_pointer (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_POINTER);
- entry = (* table->find_function) (table, key, TRUE, NULL);
+ entry = (* table->find_function) (table, key, TRUE, NULL, NULL);
if (entry == NULL)
return FALSE; /* no memory */
@@ -1366,7 +1398,7 @@ _dbus_hash_table_insert_ulong (DBusHashTable *table,
_dbus_assert (table->key_type == DBUS_HASH_ULONG);
- entry = (* table->find_function) (table, (void*) key, TRUE, NULL);
+ entry = (* table->find_function) (table, (void*) key, TRUE, NULL, NULL);
if (entry == NULL)
return FALSE; /* no memory */
@@ -1384,6 +1416,82 @@ _dbus_hash_table_insert_ulong (DBusHashTable *table,
}
/**
+ * Preallocate an opaque data blob that allows us to insert into the
+ * hash table at a later time without allocating any memory.
+ *
+ * @param table the hash table
+ * @returns the preallocated data, or #NULL if no memory
+ */
+DBusPreallocatedHash*
+_dbus_hash_table_preallocate_entry (DBusHashTable *table)
+{
+ DBusHashEntry *entry;
+
+ entry = alloc_entry (table);
+
+ return (DBusPreallocatedHash*) entry;
+}
+
+/**
+ * Frees an opaque DBusPreallocatedHash that was *not* used
+ * in order to insert into the hash table.
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ */
+void
+_dbus_hash_table_free_preallocated_entry (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (preallocated != NULL);
+
+ entry = (DBusHashEntry*) preallocated;
+
+ /* Don't use free_entry(), since this entry has no key/data */
+ _dbus_mem_pool_dealloc (table->entry_pool, entry);
+}
+
+/**
+ * Inserts a string-keyed entry into the hash table, using a
+ * preallocated data block from
+ * _dbus_hash_table_preallocate_entry(). This function cannot fail due
+ * to lack of memory. The DBusPreallocatedHash object is consumed and
+ * should not be reused or freed. Otherwise this function works
+ * just like _dbus_hash_table_insert_string().
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ * @param key the hash key
+ * @param value the value
+ */
+void
+_dbus_hash_table_insert_string_preallocated (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated,
+ char *key,
+ void *value)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_STRING);
+ _dbus_assert (preallocated != NULL);
+
+ entry = (* table->find_function) (table, key, TRUE, NULL, preallocated);
+
+ _dbus_assert (entry != NULL);
+
+ if (table->free_key_function && entry->key != key)
+ (* table->free_key_function) (entry->key);
+
+ if (table->free_value_function && entry->value != value)
+ (* table->free_value_function) (entry->value);
+
+ entry->key = key;
+ entry->value = value;
+}
+
+/**
* Gets the number of hash entries in a hash table.
*
* @param table the hash table.
diff --git a/dbus/dbus-hash.h b/dbus/dbus-hash.h
index b9efcebb..566d4021 100644
--- a/dbus/dbus-hash.h
+++ b/dbus/dbus-hash.h
@@ -108,6 +108,17 @@ dbus_bool_t _dbus_hash_table_insert_ulong (DBusHashTable *table,
int _dbus_hash_table_get_n_entries (DBusHashTable *table);
+/* Preallocation */
+typedef struct DBusPreallocatedHash DBusPreallocatedHash;
+
+DBusPreallocatedHash *_dbus_hash_table_preallocate_entry (DBusHashTable *table);
+void _dbus_hash_table_free_preallocated_entry (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated);
+void _dbus_hash_table_insert_string_preallocated (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated,
+ char *key,
+ void *value);
+
DBUS_END_DECLS;
diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c
index c6205971..5f4c67ca 100644
--- a/dbus/dbus-list.c
+++ b/dbus/dbus-list.c
@@ -372,6 +372,42 @@ _dbus_list_insert_after (DBusList **list,
}
/**
+ * Inserts a link into the list before the given existing link.
+ *
+ * @param list the list to modify
+ * @param before_this_link existing link to insert before, or #NULL to append
+ * @param link the link to insert
+ */
+void
+_dbus_list_insert_before_link (DBusList **list,
+ DBusList *before_this_link,
+ DBusList *link)
+{
+ if (before_this_link == NULL)
+ _dbus_list_append_link (list, link);
+ else
+ link_before (list, before_this_link, link);
+}
+
+/**
+ * Inserts a link into the list after the given existing link.
+ *
+ * @param list the list to modify
+ * @param after_this_link existing link to insert after, or #NULL to prepend
+ * @param link the link to insert
+ */
+void
+_dbus_list_insert_after_link (DBusList **list,
+ DBusList *after_this_link,
+ DBusList *link)
+{
+ if (after_this_link == NULL)
+ _dbus_list_prepend_link (list, link);
+ else
+ link_after (list, after_this_link, link);
+}
+
+/**
* Removes a value from the list. Only removes the
* first value equal to the given data pointer,
* even if multiple values exist which match.
diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h
index 5f42ca3c..ad74dfd0 100644
--- a/dbus/dbus-list.h
+++ b/dbus/dbus-list.h
@@ -38,44 +38,50 @@ struct DBusList
DBusList *next; /**< Next list node. */
void *data; /**< Data stored at this element. */
};
+dbus_bool_t _dbus_list_append (DBusList **list,
+ void *data);
+dbus_bool_t _dbus_list_prepend (DBusList **list,
+ void *data);
+dbus_bool_t _dbus_list_insert_before (DBusList **list,
+ DBusList *before_this_link,
+ void *data);
+dbus_bool_t _dbus_list_insert_after (DBusList **list,
+ DBusList *after_this_link,
+ void *data);
+void _dbus_list_insert_before_link (DBusList **list,
+ DBusList *before_this_link,
+ DBusList *link);
+void _dbus_list_insert_after_link (DBusList **list,
+ DBusList *after_this_link,
+ DBusList *link);
+dbus_bool_t _dbus_list_remove (DBusList **list,
+ void *data);
+dbus_bool_t _dbus_list_remove_last (DBusList **list,
+ void *data);
+void _dbus_list_remove_link (DBusList **list,
+ DBusList *link);
+void _dbus_list_clear (DBusList **list);
+DBusList* _dbus_list_get_first_link (DBusList **list);
+DBusList* _dbus_list_get_last_link (DBusList **list);
+void* _dbus_list_get_last (DBusList **list);
+void* _dbus_list_get_first (DBusList **list);
+void* _dbus_list_pop_first (DBusList **list);
+void* _dbus_list_pop_last (DBusList **list);
+DBusList* _dbus_list_pop_first_link (DBusList **list);
+DBusList* _dbus_list_pop_last_link (DBusList **list);
+dbus_bool_t _dbus_list_copy (DBusList **list,
+ DBusList **dest);
+int _dbus_list_get_length (DBusList **list);
+DBusList* _dbus_list_alloc_link (void *data);
+void _dbus_list_free_link (DBusList *link);
+void _dbus_list_append_link (DBusList **list,
+ DBusList *link);
+void _dbus_list_prepend_link (DBusList **list,
+ DBusList *link);
+dbus_bool_t _dbus_list_length_is_one (DBusList **list);
-dbus_bool_t _dbus_list_append (DBusList **list,
- void *data);
-dbus_bool_t _dbus_list_prepend (DBusList **list,
- void *data);
-dbus_bool_t _dbus_list_insert_before (DBusList **list,
- DBusList *before_this_link,
- void *data);
-dbus_bool_t _dbus_list_insert_after (DBusList **list,
- DBusList *after_this_link,
- void *data);
-dbus_bool_t _dbus_list_remove (DBusList **list,
- void *data);
-dbus_bool_t _dbus_list_remove_last (DBusList **list,
- void *data);
-void _dbus_list_remove_link (DBusList **list,
- DBusList *link);
-void _dbus_list_clear (DBusList **list);
-DBusList* _dbus_list_get_first_link (DBusList **list);
-DBusList* _dbus_list_get_last_link (DBusList **list);
-void* _dbus_list_get_last (DBusList **list);
-void* _dbus_list_get_first (DBusList **list);
-void* _dbus_list_pop_first (DBusList **list);
-void* _dbus_list_pop_last (DBusList **list);
-DBusList* _dbus_list_pop_first_link (DBusList **list);
-DBusList* _dbus_list_pop_last_link (DBusList **list);
-dbus_bool_t _dbus_list_copy (DBusList **list,
- DBusList **dest);
-int _dbus_list_get_length (DBusList **list);
-DBusList* _dbus_list_alloc_link (void *data);
-void _dbus_list_free_link (DBusList *link);
-void _dbus_list_append_link (DBusList **list,
- DBusList *link);
-void _dbus_list_prepend_link (DBusList **list,
- DBusList *link);
-dbus_bool_t _dbus_list_length_is_one (DBusList **list);
void _dbus_list_foreach (DBusList **list,
DBusForeachFunction function,
diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c
index 87e1ffc2..0e08cd78 100644
--- a/dbus/dbus-spawn.c
+++ b/dbus/dbus-spawn.c
@@ -879,6 +879,8 @@ check_babysit_events (pid_t grandchild_pid,
else if (ret == grandchild_pid)
{
/* Child exited */
+ _dbus_verbose ("reaped child pid %ld\n", (long) ret);
+
write_status_and_exit (parent_pipe, status);
}
else
@@ -890,13 +892,13 @@ check_babysit_events (pid_t grandchild_pid,
if (revents & _DBUS_POLLIN)
{
- /* Data to read from parent */
-
+ _dbus_verbose ("babysitter got POLLIN from parent pipe\n");
}
if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP))
{
/* Parent is gone, so we just exit */
+ _dbus_verbose ("babysitter got POLLERR or POLLHUP from parent\n");
_exit (0);
}
}
diff --git a/doc/dbus-specification.sgml b/doc/dbus-specification.sgml
index 109e964f..a464a0d6 100644
--- a/doc/dbus-specification.sgml
+++ b/doc/dbus-specification.sgml
@@ -1084,13 +1084,19 @@
</para>
<para>
The message bus keeps track of a set of
- <firstterm>services</firstterm>. A service is simply a name, such
- as <literal>com.yoyodyne.Screensaver</literal>, which can be
- <firstterm>owned</firstterm> by one of the connected applications.
- The message bus itself always owns the special service
+ <firstterm>services</firstterm>. A service is simply a name, such as
+ <literal>com.yoyodyne.Screensaver</literal>, which can be
+ <firstterm>owned</firstterm> by one or more of the connected
+ applications. The message bus itself always owns the special service
<literal>org.freedesktop.DBus</literal>.
</para>
<para>
+ Services may have <firstterm>secondary owners</firstterm>. Secondary owners
+ of a service are kept in a queue; if the primary owner of a service
+ disconnects, or releases the service, the next secondary owner becomes
+ the new owner of the service.
+ </para>
+ <para>
Messages may have a <literal>srvc</literal> field (see <xref
linkend="message-protocol-header-fields">). When the message bus
receives a message, if the <literal>srvc</literal> field is absent, the
@@ -1911,17 +1917,27 @@
</para>
</glossdef>
</glossentry>
+ <glossentry id="term-secondary-owner"><glossterm>Secondary service owner</glossterm>
+ <glossdef>
+ <para>
+ Each service has a primary owner; messages sent to the service name
+ go to the primary owner. However, certain services also maintain
+ a queue of secondary owners "waiting in the wings." If
+ the primary owner releases the service, then the first secondary
+ owner in the queue automatically becomes the primary owner.
+ </para>
+ </glossdef>
+ </glossentry>
<glossentry id="term-service"><glossterm>Service</glossterm>
<glossdef>
<para>
- A service is simply a named application that other
- applications can refer to. For example, the
- hypothetical <literal>com.yoyodyne.Screensaver</literal>
- service might accept messages that affect
- a screensaver from Yoyodyne Corporation.
- An application is said to <firstterm>own</firstterm>
- a service if the message bus has associated the
- application with the service name.
+ A service is simply a named list of applications. For example, the
+ hypothetical <literal>com.yoyodyne.Screensaver</literal> service might
+ accept messages that affect a screensaver from Yoyodyne Corporation.
+ An application is said to <firstterm>own</firstterm> a service if the
+ message bus has associated the application with the service name.
+ Services may also have <firstterm>secondary owners</firstterm> (see
+ <xref linkend="term-secondary-owner">).
</para>
</glossdef>
</glossentry>
diff --git a/test/test-service.c b/test/test-service.c
index 9d5eceef..ec2e8dc7 100644
--- a/test/test-service.c
+++ b/test/test-service.c
@@ -17,7 +17,7 @@ quit (void)
static void
die (const char *message)
{
- fprintf (stderr, "%s", message);
+ fprintf (stderr, "*** %s", message);
exit (1);
}
@@ -110,7 +110,7 @@ main (int argc,
connection = dbus_bus_get (DBUS_BUS_ACTIVATION, &error);
if (connection == NULL)
{
- fprintf (stderr, "Failed to open connection to activating message bus: %s\n",
+ fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n",
error.message);
dbus_error_free (&error);
return 1;
@@ -135,7 +135,7 @@ main (int argc,
0, &error);
if (dbus_error_is_set (&error))
{
- fprintf (stderr, "Failed to acquire service: %s\n",
+ fprintf (stderr, "*** Failed to acquire service: %s\n",
error.message);
dbus_error_free (&error);
exit (1);