diff options
author | Havoc Pennington <hp@redhat.com> | 2003-04-11 00:03:06 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2003-04-11 00:03:06 +0000 |
commit | 6be547d32f018c23ba56426a0bccd08baa2cf440 (patch) | |
tree | 00edf1d2628538d66d3f5db7a998c18ffc30ce04 | |
parent | 7074a2469521949c706f3b9d789d7c8eb9f3ac85 (diff) | |
download | dbus-6be547d32f018c23ba56426a0bccd08baa2cf440.tar.gz |
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-- | ChangeLog | 31 | ||||
-rw-r--r-- | bus/activation.c | 106 | ||||
-rw-r--r-- | bus/connection.c | 113 | ||||
-rw-r--r-- | bus/connection.h | 42 | ||||
-rw-r--r-- | bus/dispatch.c | 98 | ||||
-rw-r--r-- | bus/driver.c | 87 | ||||
-rw-r--r-- | bus/services.c | 414 | ||||
-rw-r--r-- | bus/services.h | 71 | ||||
-rw-r--r-- | dbus/dbus-connection.c | 5 | ||||
-rw-r--r-- | dbus/dbus-hash.c | 256 | ||||
-rw-r--r-- | dbus/dbus-hash.h | 11 | ||||
-rw-r--r-- | dbus/dbus-list.c | 36 | ||||
-rw-r--r-- | dbus/dbus-list.h | 76 | ||||
-rw-r--r-- | dbus/dbus-spawn.c | 6 | ||||
-rw-r--r-- | doc/dbus-specification.sgml | 40 | ||||
-rw-r--r-- | test/test-service.c | 6 |
16 files changed, 1070 insertions, 328 deletions
@@ -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); |