diff options
author | Havoc Pennington <hp@redhat.com> | 2005-02-26 06:37:46 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2005-02-26 06:37:46 +0000 |
commit | ee27481d7b7d6d9a4f41b7d641a2618dedf676dd (patch) | |
tree | 73590d01384d365c867ffa7d43e28a9b2a432c87 /dbus/dbus-connection.c | |
parent | 7ce7502e1ae23766ba40105327de787c2d1cef9d (diff) |
2005-02-26 Havoc Pennington <hp@redhat.com>
* doc/TODO: remove the "guid" item
* test/glib/test-profile.c (no_bus_thread_func): use open_private
(with_bus_thread_func): use open_private
* dbus/dbus-connection.c (dbus_connection_open_private): new
function that works like the old dbus_connection_open()
(dbus_connection_open): now returns an existing connection if
possible
* dbus/dbus-server-unix.c (handle_new_client_fd_and_unlock): pass
through the GUID to the transport
* dbus/dbus-server.c (_dbus_server_init_base): keep around the
GUID in hex-encoded form.
* dbus/dbus-server-debug-pipe.c (_dbus_transport_debug_pipe_new):
pass GUID argument in to the transport
* dbus/dbus-transport-unix.c (_dbus_transport_new_for_fd): add
guid argument
* dbus/dbus-transport.c (_dbus_transport_init_base): add guid argument
* dbus/dbus-auth.c (_dbus_auth_server_new): add guid argument
Diffstat (limited to 'dbus/dbus-connection.c')
-rw-r--r-- | dbus/dbus-connection.c | 382 |
1 files changed, 351 insertions, 31 deletions
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 9c57edc9..92e0331b 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -233,6 +233,10 @@ struct DBusConnection * for the global linked list mempool lock */ DBusObjectTree *objects; /**< Object path handlers registered with this connection */ + + char *server_guid; /**< GUID of server if we are in shared_connections, #NULL if server GUID is unknown or connection is private */ + + unsigned int shareable : 1; /**< #TRUE if connection can go in shared_connections once we know the GUID */ unsigned int dispatch_acquired : 1; /**< Someone has dispatch path (can drain incoming queue) */ unsigned int io_path_acquired : 1; /**< Someone has transport io path (can use the transport to read/write messages) */ @@ -1175,6 +1179,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport) connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */ connection->objects = objects; connection->exit_on_disconnect = FALSE; + connection->shareable = FALSE; #ifndef DBUS_DISABLE_CHECKS connection->generation = _dbus_current_generation; #endif @@ -1363,6 +1368,301 @@ _dbus_connection_handle_watch (DBusWatch *watch, return retval; } +_DBUS_DEFINE_GLOBAL_LOCK (shared_connections); +static DBusHashTable *shared_connections = NULL; + +static void +shared_connections_shutdown (void *data) +{ + _DBUS_LOCK (shared_connections); + + _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) == 0); + _dbus_hash_table_unref (shared_connections); + shared_connections = NULL; + + _DBUS_UNLOCK (shared_connections); +} + +static dbus_bool_t +connection_lookup_shared (DBusAddressEntry *entry, + DBusConnection **result) +{ + _dbus_verbose ("checking for existing connection\n"); + + *result = NULL; + + _DBUS_LOCK (shared_connections); + + if (shared_connections == NULL) + { + _dbus_verbose ("creating shared_connections hash table\n"); + + shared_connections = _dbus_hash_table_new (DBUS_HASH_STRING, + dbus_free, + NULL); + if (shared_connections == NULL) + { + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + if (!_dbus_register_shutdown_func (shared_connections_shutdown, NULL)) + { + _dbus_hash_table_unref (shared_connections); + shared_connections = NULL; + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + _dbus_verbose (" successfully created shared_connections\n"); + + _DBUS_UNLOCK (shared_connections); + return TRUE; /* no point looking up in the hash we just made */ + } + else + { + const char *guid; + + guid = dbus_address_entry_get_value (entry, "guid"); + + if (guid != NULL) + { + *result = _dbus_hash_table_lookup_string (shared_connections, + guid); + + if (*result) + { + /* The DBusConnection can't have been disconnected + * between the lookup and this code, because the + * disconnection will take the shared_connections lock to + * remove the connection. It can't have been finalized + * since you have to disconnect prior to finalize. + * + * Thus it's safe to ref the connection. + */ + dbus_connection_ref (*result); + + _dbus_verbose ("looked up existing connection to server guid %s\n", + guid); + } + } + + _DBUS_UNLOCK (shared_connections); + return TRUE; + } +} + +static dbus_bool_t +connection_record_shared_unlocked (DBusConnection *connection, + const char *guid) +{ + char *guid_key; + char *guid_in_connection; + + /* A separate copy of the key is required in the hash table, because + * we don't have a lock on the connection when we are doing a hash + * lookup. + */ + + _dbus_assert (connection->server_guid == NULL); + _dbus_assert (connection->shareable); + + guid_key = _dbus_strdup (guid); + if (guid_key == NULL) + return FALSE; + + guid_in_connection = _dbus_strdup (guid); + if (guid_in_connection == NULL) + { + dbus_free (guid_key); + return FALSE; + } + + _DBUS_LOCK (shared_connections); + _dbus_assert (shared_connections != NULL); + + if (!_dbus_hash_table_insert_string (shared_connections, + guid_key, connection)) + { + dbus_free (guid_key); + dbus_free (guid_in_connection); + _DBUS_UNLOCK (shared_connections); + return FALSE; + } + + connection->server_guid = guid_in_connection; + + _dbus_verbose ("stored connection to %s to be shared\n", + connection->server_guid); + + _DBUS_UNLOCK (shared_connections); + + _dbus_assert (connection->server_guid != NULL); + + return TRUE; +} + +static void +connection_forget_shared_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + + if (connection->server_guid == NULL) + return; + + _dbus_verbose ("dropping connection to %s out of the shared table\n", + connection->server_guid); + + _DBUS_LOCK (shared_connections); + + if (!_dbus_hash_table_remove_string (shared_connections, + connection->server_guid)) + _dbus_assert_not_reached ("connection was not in the shared table"); + + dbus_free (connection->server_guid); + connection->server_guid = NULL; + + _DBUS_UNLOCK (shared_connections); +} + +static DBusConnection* +connection_try_from_address_entry (DBusAddressEntry *entry, + DBusError *error) +{ + DBusTransport *transport; + DBusConnection *connection; + + transport = _dbus_transport_open (entry, error); + + if (transport == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return NULL; + } + + connection = _dbus_connection_new_for_transport (transport); + + _dbus_transport_unref (transport); + + if (connection == NULL) + { + _DBUS_SET_OOM (error); + return NULL; + } + +#ifndef DBUS_DISABLE_CHECKS + _dbus_assert (!connection->have_connection_lock); +#endif + return connection; +} + +/* + * If the shared parameter is true, then any existing connection will + * be used (and if a new connection is created, it will be available + * for use by others). If the shared parameter is false, a new + * connection will always be created, and the new connection will + * never be returned to other callers. + * + * @param address the address + * @param shared whether the connection is shared or private + * @param error error return + * @returns the connection or #NULL on error + */ +static DBusConnection* +_dbus_connection_open_internal (const char *address, + dbus_bool_t shared, + DBusError *error) +{ + DBusConnection *connection; + DBusAddressEntry **entries; + DBusError tmp_error; + DBusError first_error; + int len, i; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("opening %s connection to: %s\n", + shared ? "shared" : "private", address); + + if (!dbus_parse_address (address, &entries, &len, error)) + return NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + connection = NULL; + + dbus_error_init (&tmp_error); + dbus_error_init (&first_error); + for (i = 0; i < len; i++) + { + if (shared) + { + if (!connection_lookup_shared (entries[i], &connection)) + _DBUS_SET_OOM (&tmp_error); + } + + if (connection == NULL) + { + connection = connection_try_from_address_entry (entries[i], + &tmp_error); + + if (connection != NULL && shared) + { + const char *guid; + + connection->shareable = TRUE; + + guid = dbus_address_entry_get_value (entries[i], "guid"); + + /* we don't have a connection lock but we know nobody + * else has a handle to the connection + */ + + if (guid && + !connection_record_shared_unlocked (connection, guid)) + { + _DBUS_SET_OOM (&tmp_error); + dbus_connection_disconnect (connection); + dbus_connection_unref (connection); + connection = NULL; + } + + /* but as of now the connection is possibly shared + * since another thread could have pulled it from the table + */ + } + } + + if (connection) + break; + + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + + if (i == 0) + dbus_move_error (&tmp_error, &first_error); + else + dbus_error_free (&tmp_error); + } + + /* NOTE we don't have a lock on a possibly-shared connection object */ + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + + if (connection == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (&first_error); + dbus_move_error (&first_error, error); + } + else + { + dbus_error_free (&first_error); + } + + dbus_address_entries_free (entries); + return connection; +} + /** @} */ /** @@ -1372,16 +1672,19 @@ _dbus_connection_handle_watch (DBusWatch *watch, */ /** - * Opens a new connection to a remote address. + * Gets a connection to a remote address. If a connection to the given + * address already exists, returns the existing connection with its + * reference count incremented. Otherwise, returns a new connection + * and saves the new connection for possible re-use if a future call + * to dbus_connection_open() asks to connect to the same server. * - * @todo specify what the address parameter is. Right now - * it's just the name of a UNIX domain socket. It should be - * something more complex that encodes which transport to use. + * Use dbus_connection_open_private() to get a dedicated connection + * not shared with other callers of dbus_connection_open(). * - * If the open fails, the function returns #NULL, and provides - * a reason for the failure in the result parameter. Pass - * #NULL for the result parameter if you aren't interested - * in the reason for failure. + * If the open fails, the function returns #NULL, and provides a + * reason for the failure in the error parameter. Pass #NULL for the + * error parameter if you aren't interested in the reason for + * failure. * * @param address the address. * @param error address where an error can be returned. @@ -1392,31 +1695,44 @@ dbus_connection_open (const char *address, DBusError *error) { DBusConnection *connection; - DBusTransport *transport; _dbus_return_val_if_fail (address != NULL, NULL); _dbus_return_val_if_error_is_set (error, NULL); - - transport = _dbus_transport_open (address, error); - if (transport == NULL) - { - _DBUS_ASSERT_ERROR_IS_SET (error); - return NULL; - } - - connection = _dbus_connection_new_for_transport (transport); - _dbus_transport_unref (transport); - - if (connection == NULL) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - return NULL; - } + connection = _dbus_connection_open_internal (address, + TRUE, + error); + + return connection; +} + +/** + * Opens a new, dedicated connection to a remote address. Unlike + * dbus_connection_open(), always creates a new connection. + * This connection will not be saved or recycled by libdbus. + * + * If the open fails, the function returns #NULL, and provides a + * reason for the failure in the error parameter. Pass #NULL for the + * error parameter if you aren't interested in the reason for + * failure. + * + * @param address the address. + * @param error address where an error can be returned. + * @returns new connection, or #NULL on failure. + */ +DBusConnection* +dbus_connection_open_private (const char *address, + DBusError *error) +{ + DBusConnection *connection; + + _dbus_return_val_if_fail (address != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); + + connection = _dbus_connection_open_internal (address, + FALSE, + error); -#ifndef DBUS_DISABLE_CHECKS - _dbus_assert (!connection->have_connection_lock); -#endif return connection; } @@ -1479,7 +1795,8 @@ _dbus_connection_last_unref (DBusConnection *connection) * you won't get the disconnected message. */ _dbus_assert (!_dbus_transport_get_is_connected (connection->transport)); - + _dbus_assert (connection->server_guid == NULL); + /* ---- We're going to call various application callbacks here, hope it doesn't break anything... */ _dbus_object_tree_free_all_unlocked (connection->objects); @@ -1529,7 +1846,7 @@ _dbus_connection_last_unref (DBusConnection *connection) _dbus_list_clear (&connection->incoming_messages); _dbus_counter_unref (connection->outgoing_counter); - + _dbus_transport_unref (connection->transport); if (connection->disconnect_message_link) @@ -1620,6 +1937,7 @@ dbus_connection_disconnect (DBusConnection *connection) _dbus_verbose ("Disconnecting %p\n", connection); CONNECTION_LOCK (connection); + _dbus_transport_disconnect (connection->transport); _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); @@ -2838,7 +3156,9 @@ _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection) { _dbus_verbose ("Sending disconnect message from %s\n", _DBUS_FUNCTION_NAME); - + + connection_forget_shared_unlocked (connection); + /* We haven't sent the disconnect message already, * and all real messages have been queued up. */ |