From 65c020eebcaec72112f6e5aa2c5e9caee29fc5df Mon Sep 17 00:00:00 2001 From: Philip Rauwolf Date: Fri, 24 May 2013 16:17:14 +0200 Subject: [PATCH] This patch fixes timeouts for asynchronous method calls (sent via "dbus_connection_send_with_reply"). Formerly, if the dbus iteration was done via "dbus_connection_read_write_dispatch", and any asynchronous method call message with timeout was sent, but the target service did not answer properly (e.g. by returning "DBUS_HANDLER_RESULT_HANDLED" despite not sending a reply), the asynchronous call did NEVER return, not even when the associated timeout expired. Reason was that during the iteration the timeouts of asynchronous calls were never checked again. --- dbus/dbus-connection.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 66315b3..69cb89a 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -3615,6 +3615,74 @@ dbus_connection_flush (DBusConnection *connection) _dbus_verbose ("end\n"); } +static int _timeouts_check(DBusConnection *connection, int timeout_milliseconds) { + int timeout_interval; + DBusHashIter iter; + DBusPendingCall *pending; + DBusTimeout* timeout; + + _dbus_hash_iter_init(connection->pending_replies, &iter); + + while(_dbus_hash_iter_next(&iter)) + { + pending = _dbus_hash_iter_get_value(&iter); + + if (_dbus_pending_call_is_timeout_added_unlocked(pending)) { + timeout = _dbus_pending_call_get_timeout_unlocked(pending); + if(dbus_timeout_get_enabled(timeout)) { + long current_tv_sec, current_tv_usec; + _dbus_get_monotonic_time(¤t_tv_sec, ¤t_tv_usec); + timeout_interval = dbus_timeout_get_interval(timeout); + timeout_milliseconds = timeout_milliseconds < timeout_interval ? timeout_milliseconds : timeout_interval; + } + + if(timeout_milliseconds < 0) { + timeout_milliseconds = 0; + break; + } + } + } + + return timeout_milliseconds; +} + +static void _timeouts_handle_helper(void *element, void *data) { + dbus_timeout_handle((DBusTimeout*) element); +} + +static void _timeouts_handle(DBusConnection *connection, int iteration_time_millis) { + DBusHashIter iter; + DBusPendingCall *pending; + DBusList *timeout_list; + DBusTimeout* timeout; + int timeout_interval; + + _dbus_hash_iter_init(connection->pending_replies, &iter); + + timeout_list = NULL; + + while(_dbus_hash_iter_next(&iter)) + { + pending = _dbus_hash_iter_get_value(&iter); + + if (_dbus_pending_call_is_timeout_added_unlocked(pending)) { + timeout = _dbus_pending_call_get_timeout_unlocked(pending); + timeout_interval = dbus_timeout_get_interval(timeout); + if (timeout && dbus_timeout_get_enabled(timeout)) { + if (iteration_time_millis >= timeout_interval) { + _dbus_list_append(&timeout_list, timeout); + } else { + _dbus_timeout_set_interval(timeout, timeout_interval - iteration_time_millis); + } + } + } + } + + _dbus_list_foreach(&timeout_list, _timeouts_handle_helper, NULL); + + _dbus_list_clear(&timeout_list); +} + /** * This function implements dbus_connection_read_write_dispatch() and * dbus_connection_read_write() (they pass a different value for the @@ -3633,6 +3701,12 @@ _dbus_connection_read_write_dispatch (DBusConnection *connection, DBusDispatchStatus dstatus; dbus_bool_t progress_possible; + long iteration_start_sec; + long iteration_start_usec; + long iteration_end_sec; + long iteration_end_usec; + int iteration_time_millis; + /* Need to grab a ref here in case we're a private connection and * the user drops the last ref in a handler we call; see bug * https://bugs.freedesktop.org/show_bug.cgi?id=15635 @@ -3655,6 +3729,11 @@ _dbus_connection_read_write_dispatch (DBusConnection *connection, else { CONNECTION_LOCK (connection); + + _dbus_get_monotonic_time (&iteration_start_sec, &iteration_start_usec); + + timeout_milliseconds = _timeouts_check(connection, timeout_milliseconds); + if (_dbus_connection_get_is_connected_unlocked (connection)) { _dbus_verbose ("doing iteration\n"); @@ -3665,8 +3744,14 @@ _dbus_connection_read_write_dispatch (DBusConnection *connection, DBUS_ITERATION_BLOCK, timeout_milliseconds); } + + _dbus_get_monotonic_time (&iteration_end_sec, &iteration_end_usec); + + iteration_time_millis = (iteration_end_sec - iteration_start_sec) * 1000 + (iteration_end_usec - iteration_start_usec) / 1000; + + _timeouts_handle(connection, iteration_time_millis); } - + HAVE_LOCK_CHECK (connection); /* If we can dispatch, we can make progress until the Disconnected message * has been processed; if we can only read/write, we can make progress -- 1.7.11.7