diff options
author | Havoc Pennington <hp@redhat.com> | 2002-12-24 06:37:33 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2002-12-24 06:37:33 +0000 |
commit | 17fbe2b702cdc880abd6cbe117e620b6432f42e0 (patch) | |
tree | 9dc357f6d6c5cd7dd4bfa2bc0dee1760d4ac366a /dbus/dbus-connection.c | |
parent | 7af22a5ef9460af0f6afc2f1704d44b2e4c18ead (diff) |
2002-12-24 Havoc Pennington <hp@pobox.com>
* glib/dbus-gthread.c: fix include
* glib/dbus-glib.h: rename DBusMessageHandler for now.
I think glib API needs to change, though, as you don't
want to use DBusMessageFunction, you want to use the
DBusMessageHandler object. Probably
dbus_connection_open_with_g_main_loop()
and dbus_connection_setup_g_main_loop() or something like that
(but think of better names...) that just create a connection
that has watch/timeout functions etc. already set up.
* dbus/dbus-connection.c
(dbus_connection_send_message_with_reply): new function just to
show how the message handler helps us deal with replies.
* dbus/dbus-list.c (_dbus_list_remove_last): new function
* dbus/dbus-string.c (_dbus_string_test): free a string that
wasn't
* dbus/dbus-hash.c: use memory pools for the hash entries
(rebuild_table): be more paranoid about overflow, and
shrink table when we can
(_dbus_hash_test): reduce number of sprintfs and write
valid C89. Add tests for case where we grow and then
shrink the hash table.
* dbus/dbus-mempool.h, dbus/dbus-mempool.c: memory pools
* dbus/dbus-connection.c (dbus_connection_register_handler)
(dbus_connection_unregister_handler): new functions
* dbus/dbus-message.c (dbus_message_get_name): new
* dbus/dbus-list.c: fix docs typo
* dbus/dbus-message-handler.h, dbus/dbus-message-handler.c:
an object representing a handler for messages.
Diffstat (limited to 'dbus/dbus-connection.c')
-rw-r--r-- | dbus/dbus-connection.c | 413 |
1 files changed, 411 insertions, 2 deletions
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 3c6df57f..4a40f0b0 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -27,6 +27,7 @@ #include "dbus-watch.h" #include "dbus-connection-internal.h" #include "dbus-list.h" +#include "dbus-hash.h" #include "dbus-message-internal.h" /** @@ -75,6 +76,10 @@ struct DBusConnection DBusConnectionErrorFunction error_function; /**< Callback for errors. */ void *error_data; /**< Data for error callback. */ DBusFreeFunction error_free_data_function; /**< Free function for error callback data. */ + DBusHashTable *handler_table; /**< Table of registered DBusMessageHandler */ + DBusList *filter_list; /**< List of filters. */ + int filters_serial; /**< Increments when the list of filters is changed. */ + int handlers_serial; /**< Increments when the handler table is changed. */ }; /** @@ -270,13 +275,21 @@ _dbus_connection_new_for_transport (DBusTransport *transport) { DBusConnection *connection; DBusWatchList *watch_list; + DBusHashTable *handler_table; watch_list = NULL; connection = NULL; + handler_table = NULL; watch_list = _dbus_watch_list_new (); if (watch_list == NULL) goto error; + + handler_table = + _dbus_hash_table_new (DBUS_HASH_STRING, + dbus_free, NULL); + if (handler_table == NULL) + goto error; connection = dbus_new0 (DBusConnection, 1); if (connection == NULL) @@ -285,7 +298,9 @@ _dbus_connection_new_for_transport (DBusTransport *transport) connection->refcount = 1; connection->transport = transport; connection->watches = watch_list; - + connection->handler_table = handler_table; + connection->filter_list = NULL; + _dbus_transport_ref (transport); _dbus_transport_set_connection (transport, connection); @@ -293,14 +308,56 @@ _dbus_connection_new_for_transport (DBusTransport *transport) error: - _dbus_assert (connection == NULL); + if (connection != NULL) + dbus_free (connection); + if (handler_table) + _dbus_hash_table_unref (handler_table); + if (watch_list) _dbus_watch_list_free (watch_list); return NULL; } +/** + * Used to notify a connection when a DBusMessageHandler is + * destroyed, so the connection can drop any reference + * to the handler. + * + * @param connection the connection + * @param handler the handler + */ +void +_dbus_connection_handler_destroyed (DBusConnection *connection, + DBusMessageHandler *handler) +{ + DBusHashIter iter; + DBusList *link; + + _dbus_hash_iter_init (connection->handler_table, &iter); + while (_dbus_hash_iter_next (&iter)) + { + DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter); + + if (h == handler) + _dbus_hash_iter_remove_entry (&iter); + } + + link = _dbus_list_get_first_link (&connection->filter_list); + while (link != NULL) + { + DBusMessageHandler *h = link->data; + DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); + + if (h == handler) + _dbus_list_remove_link (&connection->filter_list, + link); + + link = next; + } +} + /** @} */ /** @@ -377,11 +434,38 @@ dbus_connection_unref (DBusConnection *connection) connection->refcount -= 1; if (connection->refcount == 0) { + DBusHashIter iter; + DBusList *link; + /* free error data as a side effect */ dbus_connection_set_error_function (connection, NULL, NULL, NULL); _dbus_watch_list_free (connection->watches); + + _dbus_hash_iter_init (connection->handler_table, &iter); + while (_dbus_hash_iter_next (&iter)) + { + DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter); + + _dbus_message_handler_remove_connection (h, connection); + } + + link = _dbus_list_get_first_link (&connection->filter_list); + while (link != NULL) + { + DBusMessageHandler *h = link->data; + DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); + + _dbus_message_handler_remove_connection (h, connection); + + link = next; + } + + _dbus_hash_table_unref (connection->handler_table); + connection->handler_table = NULL; + + _dbus_list_clear (&connection->filter_list); _dbus_list_foreach (&connection->outgoing_messages, (DBusForeachFunction) dbus_message_unref, @@ -471,6 +555,66 @@ dbus_connection_send_message (DBusConnection *connection, } /** + * Queues a message to send, as with dbus_connection_send_message(), + * but also sets up a DBusMessageHandler to receive a reply to the + * message. If no reply is received in the given timeout_milliseconds, + * expires the pending reply and sends the DBusMessageHandler a + * synthetic error reply (generated in-process, not by the remote + * application) indicating that a timeout occurred. + * + * Reply handlers see their replies after message filters see them, + * but before message handlers added with + * dbus_connection_register_handler() see them, regardless of the + * reply message's name. Reply handlers are only handed a single + * message as a reply, after a reply has been seen the handler is + * removed. If a filter filters out the reply before the handler sees + * it, the handler is not removed but the timeout will immediately + * fire again. If a filter was dumb and kept removing the timeout + * reply then we'd get in an infinite loop. + * + * If #NULL is passed for the reply_handler, the timeout reply will + * still be generated and placed into the message queue, but no + * specific message handler will receive the reply. + * + * If -1 is passed for the timeout, a sane default timeout is used. -1 + * is typically the best value for the timeout for this reason, unless + * you want a very short or very long timeout. There is no way to + * avoid a timeout entirely, other than passing INT_MAX for the + * timeout to postpone it indefinitely. + * + * @param connection the connection + * @param message the message to send + * @param reply_handler message handler expecting the reply, or #NULL + * @param timeout_milliseconds timeout in milliseconds or -1 for default + * @param result return location for result code + * @returns #TRUE if the message is successfully queued, #FALSE if no memory. + * + * @todo this function isn't implemented because we need message serials + * and other slightly more rich DBusMessage implementation in order to + * implement it. The basic idea will be to keep a hash of serials we're + * expecting a reply to, and also to add a way to tell GLib or Qt to + * install a timeout. Then install a timeout which is the shortest + * timeout of any pending reply. + * + * @todo implement non-reentrant "block for reply" variant. i.e. send + * a message, block until we get a reply, then pull reply out of + * message queue and return it, *without dispatching any handlers for + * any other messages* - used for non-reentrant "method calls" We can + * block properly for this using _dbus_connection_do_iteration(). + * + */ +dbus_bool_t +dbus_connection_send_message_with_reply (DBusConnection *connection, + DBusMessage *message, + DBusMessageHandler *reply_handler, + int timeout_milliseconds, + DBusResultCode *result) +{ + /* FIXME */ + return dbus_connection_send_message (connection, message, result); +} + +/** * Blocks until the outgoing message queue is empty. * * @param connection the connection. @@ -532,6 +676,90 @@ dbus_connection_pop_message (DBusConnection *connection) } /** + * Pops the first-received message from the current incoming message + * queue, runs any handlers for it, then unrefs the message. + * + * @param connection the connection + * @returns #TRUE if the queue is not empty after dispatch + * + * @todo this function is not properly robust against reentrancy, + * that is, if handlers are added/removed while dispatching + * a message, things will get messed up. + */ +dbus_bool_t +dbus_connection_dispatch_message (DBusConnection *connection) +{ + DBusMessage *message; + int filter_serial; + int handler_serial; + DBusList *link; + DBusHandlerResult result; + const char *name; + + dbus_connection_ref (connection); + + message = dbus_connection_pop_message (connection); + if (message == NULL) + return FALSE; + + filter_serial = connection->filters_serial; + handler_serial = connection->handlers_serial; + + result = DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + + link = _dbus_list_get_first_link (&connection->filter_list); + while (link != NULL) + { + DBusMessageHandler *handler = link->data; + DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); + + result = _dbus_message_handler_handle_message (handler, connection, + message); + + if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE) + goto out; + + if (filter_serial != connection->filters_serial) + { + _dbus_warn ("Message filters added or removed while dispatching filters - not currently supported!\n"); + goto out; + } + + link = next; + } + + name = dbus_message_get_name (message); + if (name != NULL) + { + DBusMessageHandler *handler; + + handler = _dbus_hash_table_lookup_string (connection->handler_table, + name); + if (handler != NULL) + { + + result = _dbus_message_handler_handle_message (handler, connection, + message); + + if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE) + goto out; + + if (handler_serial != connection->handlers_serial) + { + _dbus_warn ("Message handlers added or removed while dispatching handlers - not currently supported!\n"); + goto out; + } + } + } + + out: + dbus_connection_unref (connection); + dbus_message_unref (message); + + return connection->n_incoming > 0; +} + +/** * Sets the error handler function for the connection. * * @param connection the connection. @@ -623,4 +851,185 @@ dbus_connection_handle_watch (DBusConnection *connection, watch, condition); } +/** + * Adds a message filter. Filters are handlers that are run on + * all incoming messages, prior to the normal handlers + * registered with dbus_connection_register_handler(). + * Filters are run in the order that they were added. + * The same handler can be added as a filter more than once, in + * which case it will be run more than once. + * + * @param connection the connection + * @param handler the handler + * @returns #TRUE on success, #FALSE if not enough memory. + */ +dbus_bool_t +dbus_connection_add_filter (DBusConnection *connection, + DBusMessageHandler *handler) +{ + if (!_dbus_message_handler_add_connection (handler, connection)) + return FALSE; + + if (!_dbus_list_append (&connection->filter_list, + handler)) + { + _dbus_message_handler_remove_connection (handler, connection); + return FALSE; + } + + connection->filters_serial += 1; + + return TRUE; +} + +/** + * Removes a previously-added message filter. It is a programming + * error to call this function for a handler that has not + * been added as a filter. If the given handler was added + * more than once, only one instance of it will be removed + * (the most recently-added instance). + * + * @param connection the connection + * @param handler the handler to remove + * + */ +void +dbus_connection_remove_filter (DBusConnection *connection, + DBusMessageHandler *handler) +{ + if (!_dbus_list_remove_last (&connection->filter_list, handler)) + { + _dbus_warn ("Tried to remove a DBusConnection filter that had not been added\n"); + return; + } + + _dbus_message_handler_remove_connection (handler, connection); + + connection->filters_serial += 1; +} + +/** + * Registers a handler for a list of message names. A single handler + * can be registered for any number of message names, but each message + * name can only have one handler at a time. It's not allowed to call + * this function with the name of a message that already has a + * handler. If the function returns #FALSE, the handlers were not + * registered due to lack of memory. + * + * @param connection the connection + * @param handler the handler + * @param messages_to_handle the messages to handle + * @param n_messages the number of message names in messages_to_handle + * @returns #TRUE on success, #FALSE if no memory or another handler already exists + * + **/ +dbus_bool_t +dbus_connection_register_handler (DBusConnection *connection, + DBusMessageHandler *handler, + const char **messages_to_handle, + int n_messages) +{ + int i; + + i = 0; + while (i < n_messages) + { + DBusHashIter iter; + char *key; + + key = _dbus_strdup (messages_to_handle[i]); + if (key == NULL) + goto failed; + + if (!_dbus_hash_iter_lookup (connection->handler_table, + key, TRUE, + &iter)) + { + dbus_free (key); + goto failed; + } + + if (_dbus_hash_iter_get_value (&iter) != NULL) + { + _dbus_warn ("Bug in application: attempted to register a second handler for %s\n", + messages_to_handle[i]); + dbus_free (key); /* won't have replaced the old key with the new one */ + goto failed; + } + + if (!_dbus_message_handler_add_connection (handler, connection)) + { + _dbus_hash_iter_remove_entry (&iter); + /* key has freed on nuking the entry */ + goto failed; + } + + _dbus_hash_iter_set_value (&iter, handler); + + connection->handlers_serial += 1; + + ++i; + } + + return TRUE; + + failed: + /* unregister everything registered so far, + * so we don't fail partially + */ + dbus_connection_unregister_handler (connection, + handler, + messages_to_handle, + i); + + return FALSE; +} + +/** + * Unregisters a handler for a list of message names. The handlers + * must have been previously registered. + * + * @param connection the connection + * @param handler the handler + * @param messages_to_handle the messages to handle + * @param n_messages the number of message names in messages_to_handle + * + **/ +void +dbus_connection_unregister_handler (DBusConnection *connection, + DBusMessageHandler *handler, + const char **messages_to_handle, + int n_messages) +{ + int i; + + i = 0; + while (i < n_messages) + { + DBusHashIter iter; + + if (!_dbus_hash_iter_lookup (connection->handler_table, + (char*) messages_to_handle[i], FALSE, + &iter)) + { + _dbus_warn ("Bug in application: attempted to unregister handler for %s which was not registered\n", + messages_to_handle[i]); + } + else if (_dbus_hash_iter_get_value (&iter) != handler) + { + _dbus_warn ("Bug in application: attempted to unregister handler for %s which was registered by a different handler\n", + messages_to_handle[i]); + } + else + { + _dbus_hash_iter_remove_entry (&iter); + _dbus_message_handler_remove_connection (handler, connection); + } + + ++i; + } + + connection->handlers_serial += 1; +} + /** @} */ |