From 2a895edf6b1cad3915f5ca9e8b41f4bba780b2e0 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Fri, 20 Oct 2006 03:42:03 +0000 Subject: 2006-10-19 Havoc Pennington * more Doxygen-related fixes (lots of moving things from the public to internal section in dbus-connection.c) --- dbus/dbus-connection.c | 2065 ++++++++++++++++++++++++------------------------ 1 file changed, 1033 insertions(+), 1032 deletions(-) (limited to 'dbus/dbus-connection.c') diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 92fa15a8..a16ca54d 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -1760,6 +1760,154 @@ _dbus_connection_close_possibly_shared (DBusConnection *connection) _dbus_connection_close_possibly_shared_and_unlock (connection); } +static DBusPreallocatedSend* +_dbus_connection_preallocate_send_unlocked (DBusConnection *connection) +{ + DBusPreallocatedSend *preallocated; + + HAVE_LOCK_CHECK (connection); + + _dbus_assert (connection != NULL); + + preallocated = dbus_new (DBusPreallocatedSend, 1); + if (preallocated == NULL) + return NULL; + + if (connection->link_cache != NULL) + { + preallocated->queue_link = + _dbus_list_pop_first_link (&connection->link_cache); + preallocated->queue_link->data = NULL; + } + else + { + preallocated->queue_link = _dbus_list_alloc_link (NULL); + if (preallocated->queue_link == NULL) + goto failed_0; + } + + if (connection->link_cache != NULL) + { + preallocated->counter_link = + _dbus_list_pop_first_link (&connection->link_cache); + preallocated->counter_link->data = connection->outgoing_counter; + } + else + { + preallocated->counter_link = _dbus_list_alloc_link (connection->outgoing_counter); + if (preallocated->counter_link == NULL) + goto failed_1; + } + + _dbus_counter_ref (preallocated->counter_link->data); + + preallocated->connection = connection; + + return preallocated; + + failed_1: + _dbus_list_free_link (preallocated->queue_link); + failed_0: + dbus_free (preallocated); + + return NULL; +} + +/* Called with lock held, does not update dispatch status */ +static void +_dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + dbus_uint32_t serial; + const char *sig; + + preallocated->queue_link->data = message; + _dbus_list_prepend_link (&connection->outgoing_messages, + preallocated->queue_link); + + _dbus_message_add_size_counter_link (message, + preallocated->counter_link); + + dbus_free (preallocated); + preallocated = NULL; + + dbus_message_ref (message); + + connection->n_outgoing += 1; + + sig = dbus_message_get_signature (message); + + _dbus_verbose ("Message %p (%d %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n", + message, + dbus_message_get_type (message), + dbus_message_get_path (message) ? + dbus_message_get_path (message) : + "no path", + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface", + dbus_message_get_member (message) ? + dbus_message_get_member (message) : + "no member", + sig, + dbus_message_get_destination (message) ? + dbus_message_get_destination (message) : + "null", + connection, + connection->n_outgoing); + + if (dbus_message_get_serial (message) == 0) + { + serial = _dbus_connection_get_next_client_serial (connection); + _dbus_message_set_serial (message, serial); + if (client_serial) + *client_serial = serial; + } + else + { + if (client_serial) + *client_serial = dbus_message_get_serial (message); + } + + _dbus_verbose ("Message %p serial is %u\n", + message, dbus_message_get_serial (message)); + + _dbus_message_lock (message); + + /* Now we need to run an iteration to hopefully just write the messages + * out immediately, and otherwise get them queued up + */ + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_WRITING, + -1); + + /* If stuff is still queued up, be sure we wake up the main loop */ + if (connection->n_outgoing > 0) + _dbus_connection_wakeup_mainloop (connection); +} + +static void +_dbus_connection_send_preallocated_and_unlock (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + DBusDispatchStatus status; + + HAVE_LOCK_CHECK (connection); + + _dbus_connection_send_preallocated_unlocked_no_update (connection, + preallocated, + message, client_serial); + + _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* this calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); +} /** * Like dbus_connection_send(), but assumes the connection @@ -1794,1279 +1942,1132 @@ _dbus_connection_send_and_unlock (DBusConnection *connection, return TRUE; } -/** @} */ - /** - * @addtogroup DBusConnection + * Used internally to handle the semantics of dbus_server_set_new_connection_function(). + * If the new connection function does not ref the connection, we want to close it. * - * @{ - */ - -/** - * 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. + * A bit of a hack, probably the new connection function should have returned a value + * for whether to close, or should have had to close the connection itself if it + * didn't want it. * - * Use dbus_connection_open_private() to get a dedicated connection - * not shared with other callers of dbus_connection_open(). + * But, this works OK as long as the new connection function doesn't do anything + * crazy like keep the connection around without ref'ing it. * - * 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. + * We have to lock the connection across refcount check and close in case + * the new connection function spawns a thread that closes and unrefs. + * In that case, if the app thread + * closes and unrefs first, we'll harmlessly close again; if the app thread + * still has the ref, we'll close and then the app will close harmlessly. + * If the app unrefs without closing, the app is broken since if the + * app refs from the new connection function it is supposed to also close. * - * Because this connection is shared, no user of the connection - * may call dbus_connection_close(). However, when you are done with the - * connection you should call dbus_connection_unref(). + * If we didn't atomically check the refcount and close with the lock held + * though, we could screw this up. * - * @param address the address. - * @param error address where an error can be returned. - * @returns new connection, or #NULL on failure. + * @param connection the connection */ -DBusConnection* -dbus_connection_open (const char *address, - DBusError *error) +void +_dbus_connection_close_if_only_one_ref (DBusConnection *connection) { - 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, - TRUE, - error); + CONNECTION_LOCK (connection); + + _dbus_assert (connection->refcount.value > 0); - return connection; + if (connection->refcount.value == 1) + _dbus_connection_close_possibly_shared_and_unlock (connection); + else + CONNECTION_UNLOCK (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. + * When a function that blocks has been called with a timeout, and we + * run out of memory, the time to wait for memory is based on the + * timeout. If the caller was willing to block a long time we wait a + * relatively long time for memory, if they were only willing to block + * briefly then we retry for memory at a rapid rate. * - * When you are done with this connection, you must - * dbus_connection_close() to disconnect it, - * and dbus_connection_unref() to free the connection object. - * - * (The dbus_connection_close() can be skipped if the - * connection is already known to be disconnected, for example - * if you are inside a handler for the Disconnected signal.) - * - * @param address the address. - * @param error address where an error can be returned. - * @returns new connection, or #NULL on failure. + * @timeout_milliseconds the timeout requested for blocking */ -DBusConnection* -dbus_connection_open_private (const char *address, - DBusError *error) +static void +_dbus_memory_pause_based_on_timeout (int timeout_milliseconds) { - DBusConnection *connection; - - _dbus_return_val_if_fail (address != NULL, NULL); - _dbus_return_val_if_error_is_set (error, NULL); + if (timeout_milliseconds == -1) + _dbus_sleep_milliseconds (1000); + else if (timeout_milliseconds < 100) + ; /* just busy loop */ + else if (timeout_milliseconds <= 1000) + _dbus_sleep_milliseconds (timeout_milliseconds / 3); + else + _dbus_sleep_milliseconds (1000); +} - connection = _dbus_connection_open_internal (address, - FALSE, - error); +static DBusMessage * +generate_local_error_message (dbus_uint32_t serial, + char *error_name, + char *error_msg) +{ + DBusMessage *message; + message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); + if (!message) + goto out; - return connection; -} + if (!dbus_message_set_error_name (message, error_name)) + { + dbus_message_unref (message); + message = NULL; + goto out; + } -/** - * Increments the reference count of a DBusConnection. - * - * @param connection the connection. - * @returns the connection. - */ -DBusConnection * -dbus_connection_ref (DBusConnection *connection) -{ - _dbus_return_val_if_fail (connection != NULL, NULL); - _dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL); - - /* The connection lock is better than the global - * lock in the atomic increment fallback - */ - -#ifdef DBUS_HAVE_ATOMIC_INT - _dbus_atomic_inc (&connection->refcount); -#else - CONNECTION_LOCK (connection); - _dbus_assert (connection->refcount.value > 0); + dbus_message_set_no_reply (message, TRUE); - connection->refcount.value += 1; - CONNECTION_UNLOCK (connection); -#endif + if (!dbus_message_set_reply_serial (message, + serial)) + { + dbus_message_unref (message); + message = NULL; + goto out; + } - return connection; -} + if (error_msg != NULL) + { + DBusMessageIter iter; -static void -free_outgoing_message (void *element, - void *data) -{ - DBusMessage *message = element; - DBusConnection *connection = data; + dbus_message_iter_init_append (message, &iter); + if (!dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &error_msg)) + { + dbus_message_unref (message); + message = NULL; + goto out; + } + } - _dbus_message_remove_size_counter (message, - connection->outgoing_counter, - NULL); - dbus_message_unref (message); + out: + return message; } -/* This is run without the mutex held, but after the last reference - * to the connection has been dropped we should have no thread-related - * problems + +/* This is slightly strange since we can pop a message here without + * the dispatch lock. */ -static void -_dbus_connection_last_unref (DBusConnection *connection) +static DBusMessage* +check_for_reply_unlocked (DBusConnection *connection, + dbus_uint32_t client_serial) { DBusList *link; - _dbus_verbose ("Finalizing connection %p\n", connection); - - _dbus_assert (connection->refcount.value == 0); - - /* You have to disconnect the connection before unref:ing it. Otherwise - * 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); - - dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL); - dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL); - dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL); - - _dbus_watch_list_free (connection->watches); - connection->watches = NULL; + HAVE_LOCK_CHECK (connection); - _dbus_timeout_list_free (connection->timeouts); - connection->timeouts = NULL; + link = _dbus_list_get_first_link (&connection->incoming_messages); - _dbus_data_slot_list_free (&connection->slot_list); - - link = _dbus_list_get_first_link (&connection->filter_list); while (link != NULL) { - DBusMessageFilter *filter = link->data; - DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); + DBusMessage *reply = link->data; - filter->function = NULL; - _dbus_message_filter_unref (filter); /* calls app callback */ - link->data = NULL; - - link = next; + if (dbus_message_get_reply_serial (reply) == client_serial) + { + _dbus_list_remove_link (&connection->incoming_messages, link); + connection->n_incoming -= 1; + return reply; + } + link = _dbus_list_get_next_link (&connection->incoming_messages, link); } - _dbus_list_clear (&connection->filter_list); - - /* ---- Done with stuff that invokes application callbacks */ - _dbus_object_tree_unref (connection->objects); + return NULL; +} - _dbus_hash_table_unref (connection->pending_replies); - connection->pending_replies = NULL; - - _dbus_list_clear (&connection->filter_list); - - _dbus_list_foreach (&connection->outgoing_messages, - free_outgoing_message, - connection); - _dbus_list_clear (&connection->outgoing_messages); - - _dbus_list_foreach (&connection->incoming_messages, - (DBusForeachFunction) dbus_message_unref, - NULL); - _dbus_list_clear (&connection->incoming_messages); +static void +connection_timeout_and_complete_all_pending_calls_unlocked (DBusConnection *connection) +{ + /* We can't iterate over the hash in the normal way since we'll be + * dropping the lock for each item. So we restart the + * iter each time as we drain the hash table. + */ + + while (_dbus_hash_table_get_n_entries (connection->pending_replies) > 0) + { + DBusPendingCall *pending; + DBusHashIter iter; + + _dbus_hash_iter_init (connection->pending_replies, &iter); + _dbus_hash_iter_next (&iter); + + pending = _dbus_hash_iter_get_value (&iter); + _dbus_pending_call_ref_unlocked (pending); + + _dbus_pending_call_queue_timeout_error_unlocked (pending, + connection); + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + _dbus_hash_iter_remove_entry (&iter); - _dbus_counter_unref (connection->outgoing_counter); + _dbus_pending_call_unref_and_unlock (pending); + CONNECTION_LOCK (connection); + } + HAVE_LOCK_CHECK (connection); +} - _dbus_transport_unref (connection->transport); +static void +complete_pending_call_and_unlock (DBusConnection *connection, + DBusPendingCall *pending, + DBusMessage *message) +{ + _dbus_pending_call_set_reply_unlocked (pending, message); + _dbus_pending_call_ref_unlocked (pending); /* in case there's no app with a ref held */ + _dbus_connection_detach_pending_call_and_unlock (connection, pending); + + /* Must be called unlocked since it invokes app callback */ + _dbus_pending_call_complete (pending); + dbus_pending_call_unref (pending); +} - if (connection->disconnect_message_link) +static dbus_bool_t +check_for_reply_and_update_dispatch_unlocked (DBusConnection *connection, + DBusPendingCall *pending) +{ + DBusMessage *reply; + DBusDispatchStatus status; + + reply = check_for_reply_unlocked (connection, + _dbus_pending_call_get_reply_serial_unlocked (pending)); + if (reply != NULL) { - DBusMessage *message = connection->disconnect_message_link->data; - dbus_message_unref (message); - _dbus_list_free_link (connection->disconnect_message_link); - } + _dbus_verbose ("%s checked for reply\n", _DBUS_FUNCTION_NAME); - _dbus_list_clear (&connection->link_cache); - - _dbus_condvar_free_at_location (&connection->dispatch_cond); - _dbus_condvar_free_at_location (&connection->io_path_cond); + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n"); - _dbus_mutex_free_at_location (&connection->io_path_mutex); - _dbus_mutex_free_at_location (&connection->dispatch_mutex); + complete_pending_call_and_unlock (connection, pending, reply); + dbus_message_unref (reply); - _dbus_mutex_free_at_location (&connection->mutex); - - dbus_free (connection); + CONNECTION_LOCK (connection); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + dbus_pending_call_unref (pending); + + return TRUE; + } + + return FALSE; } /** - * Decrements the reference count of a DBusConnection, and finalizes - * it if the count reaches zero. - * - * Note: it is a bug to drop the last reference to a connection that - * is still connected. + * Blocks until a pending call times out or gets a reply. * - * For shared connections, libdbus will own a reference - * as long as the connection is connected, so you can know that either - * you don't have the last reference, or it's OK to drop the last reference. - * Most connections are shared. dbus_connection_open() and dbus_bus_get() - * return shared connections. + * Does not re-enter the main loop or run filter/path-registered + * callbacks. The reply to the message will not be seen by + * filter callbacks. * - * For private connections, the creator of the connection must arrange for - * dbus_connection_close() to be called prior to dropping the last reference. - * Private connections come from dbus_connection_open_private() or dbus_bus_get_private(). + * Returns immediately if pending call already got a reply. + * + * @todo could use performance improvements (it keeps scanning + * the whole message queue for example) * - * @param connection the connection. + * @param pending the pending call we block for a reply on */ void -dbus_connection_unref (DBusConnection *connection) +_dbus_connection_block_pending_call (DBusPendingCall *pending) { - dbus_bool_t last_unref; - - _dbus_return_if_fail (connection != NULL); - _dbus_return_if_fail (connection->generation == _dbus_current_generation); - - /* The connection lock is better than the global - * lock in the atomic increment fallback - */ - -#ifdef DBUS_HAVE_ATOMIC_INT - last_unref = (_dbus_atomic_dec (&connection->refcount) == 1); -#else - CONNECTION_LOCK (connection); - - _dbus_assert (connection->refcount.value > 0); + long start_tv_sec, start_tv_usec; + long end_tv_sec, end_tv_usec; + long tv_sec, tv_usec; + DBusDispatchStatus status; + DBusConnection *connection; + dbus_uint32_t client_serial; + int timeout_milliseconds; - connection->refcount.value -= 1; - last_unref = (connection->refcount.value == 0); + _dbus_assert (pending != NULL); -#if 0 - printf ("unref() connection %p count = %d\n", connection, connection->refcount.value); -#endif - - CONNECTION_UNLOCK (connection); -#endif - - if (last_unref) - { -#ifndef DBUS_DISABLE_CHECKS - if (_dbus_transport_get_is_connected (connection->transport)) - { - _dbus_warn_check_failed ("The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.\n%s", - connection->shareable ? - "Most likely, the application called unref() too many times and removed a reference belonging to libdbus, since this is a shared connection.\n" : - "Most likely, the application was supposed to call dbus_connection_close(), since this is a private connection.\n"); - return; - } -#endif - _dbus_connection_last_unref (connection); - } -} + if (dbus_pending_call_get_completed (pending)) + return; -/* - * Note that the transport can disconnect itself (other end drops us) - * and in that case this function never runs. So this function must - * not do anything more than disconnect the transport and update the - * dispatch status. - * - * If the transport self-disconnects, then we assume someone will - * dispatch the connection to cause the dispatch status update. - */ -static void -_dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection) -{ - DBusDispatchStatus status; + dbus_pending_call_ref (pending); /* necessary because the call could be canceled */ - HAVE_LOCK_CHECK (connection); + connection = _dbus_pending_call_get_connection_and_lock (pending); - _dbus_verbose ("Disconnecting %p\n", connection); + /* Flush message queue - note, can affect dispatch status */ + _dbus_connection_flush_unlocked (connection); - /* We need to ref because update_dispatch_status_and_unlock will unref - * the connection if it was shared and libdbus was the only remaining - * refcount holder. + client_serial = _dbus_pending_call_get_reply_serial_unlocked (pending); + + /* note that timeout_milliseconds is limited to a smallish value + * in _dbus_pending_call_new() so overflows aren't possible + * below */ - _dbus_connection_ref_unlocked (connection); + timeout_milliseconds = dbus_timeout_get_interval (_dbus_pending_call_get_timeout_unlocked (pending)); - _dbus_transport_disconnect (connection->transport); + _dbus_get_current_time (&start_tv_sec, &start_tv_usec); + end_tv_sec = start_tv_sec + timeout_milliseconds / 1000; + end_tv_usec = start_tv_usec + (timeout_milliseconds % 1000) * 1000; + end_tv_sec += end_tv_usec / _DBUS_USEC_PER_SECOND; + end_tv_usec = end_tv_usec % _DBUS_USEC_PER_SECOND; - /* This has the side effect of queuing the disconnect message link - * (unless we don't have enough memory, possibly, so don't assert it). - * After the disconnect message link is queued, dbus_bus_get/dbus_connection_open - * should never again return the newly-disconnected connection. - * - * However, we only unref the shared connection and exit_on_disconnect when - * the disconnect message reaches the head of the message queue, - * NOT when it's first queued. - */ - status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %ld sec %ld usec to %ld sec %ld usec\n", + timeout_milliseconds, + client_serial, + start_tv_sec, start_tv_usec, + end_tv_sec, end_tv_usec); - /* This calls out to user code */ - _dbus_connection_update_dispatch_status_and_unlock (connection, status); + /* check to see if we already got the data off the socket */ + /* from another blocked pending call */ + if (check_for_reply_and_update_dispatch_unlocked (connection, pending)) + return; - /* Could also call out to user code */ - dbus_connection_unref (connection); -} + /* Now we wait... */ + /* always block at least once as we know we don't have the reply yet */ + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_READING | + DBUS_ITERATION_BLOCK, + timeout_milliseconds); -/** - * Closes a private connection, so no further data can be sent or received. - * This disconnects the transport (such as a socket) underlying the - * connection. - * - * Attempts to send messages after closing a connection are safe, but will result in - * error replies generated locally in libdbus. - * - * This function does not affect the connection's reference count. It's - * safe to close a connection more than once; all calls after the - * first do nothing. It's impossible to "reopen" a connection, a - * new connection must be created. This function may result in a call - * to the DBusDispatchStatusFunction set with - * dbus_connection_set_dispatch_status_function(), as the disconnect - * message it generates needs to be dispatched. - * - * If a connection is dropped by the remote application, it will - * close itself. - * - * You must close a connection prior to releasing the last reference to - * the connection. If you dbus_connection_unref() for the last time - * without closing the connection, the results are undefined; it - * is a bug in your program and libdbus will try to print a warning. - * - * You may not close a shared connection. Connections created with - * dbus_connection_open() or dbus_bus_get() are shared. - * These connections are owned by libdbus, and applications should - * only unref them, never close them. Applications can know it is - * safe to unref these connections because libdbus will be holding a - * reference as long as the connection is open. Thus, either the - * connection is closed and it is OK to drop the last reference, - * or the connection is open and the app knows it does not have the - * last reference. - * - * Connections created with dbus_connection_open_private() or - * dbus_bus_get_private() are not kept track of or referenced by - * libdbus. The creator of these connections is responsible for - * calling dbus_connection_close() prior to releasing the last - * reference, if the connection is not already disconnected. - * - * @param connection the private (unshared) connection to close - */ -void -dbus_connection_close (DBusConnection *connection) -{ - _dbus_return_if_fail (connection != NULL); - _dbus_return_if_fail (connection->generation == _dbus_current_generation); + recheck_status: - CONNECTION_LOCK (connection); + _dbus_verbose ("%s top of recheck\n", _DBUS_FUNCTION_NAME); + + HAVE_LOCK_CHECK (connection); + + /* queue messages and get status */ -#ifndef DBUS_DISABLE_CHECKS - if (connection->shareable) - { - CONNECTION_UNLOCK (connection); + status = _dbus_connection_get_dispatch_status_unlocked (connection); - _dbus_warn_check_failed ("Applications must not close shared connections - see dbus_connection_close() docs. This is a bug in the application.\n"); + /* the get_completed() is in case a dispatch() while we were blocking + * got the reply instead of us. + */ + if (_dbus_pending_call_get_completed_unlocked (pending)) + { + _dbus_verbose ("Pending call completed by dispatch in %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + dbus_pending_call_unref (pending); return; } -#endif - _dbus_connection_close_possibly_shared_and_unlock (connection); -} - -/** - * Used internally to handle the semantics of dbus_server_set_new_connection_function(). - * If the new connection function does not ref the connection, we want to close it. - * - * A bit of a hack, probably the new connection function should have returned a value - * for whether to close, or should have had to close the connection itself if it - * didn't want it. - * - * But, this works OK as long as the new connection function doesn't do anything - * crazy like keep the connection around without ref'ing it. - * - * We have to lock the connection across refcount check and close in case - * the new connection function spawns a thread that closes and unrefs. - * In that case, if the app thread - * closes and unrefs first, we'll harmlessly close again; if the app thread - * still has the ref, we'll close and then the app will close harmlessly. - * If the app unrefs without closing, the app is broken since if the - * app refs from the new connection function it is supposed to also close. - * - * If we didn't atomically check the refcount and close with the lock held - * though, we could screw this up. - * - * @param connection the connection - */ -void -_dbus_connection_close_if_only_one_ref (DBusConnection *connection) -{ - CONNECTION_LOCK (connection); + if (status == DBUS_DISPATCH_DATA_REMAINS) { + if (check_for_reply_and_update_dispatch_unlocked (connection, pending)) + return; + } - _dbus_assert (connection->refcount.value > 0); + _dbus_get_current_time (&tv_sec, &tv_usec); + + if (!_dbus_connection_get_is_connected_unlocked (connection)) + { + DBusMessage *error_msg; - if (connection->refcount.value == 1) - _dbus_connection_close_possibly_shared_and_unlock (connection); - else - CONNECTION_UNLOCK (connection); -} + error_msg = generate_local_error_message (client_serial, + DBUS_ERROR_DISCONNECTED, + "Connection was disconnected before a reply was received"); -static dbus_bool_t -_dbus_connection_get_is_connected_unlocked (DBusConnection *connection) -{ - HAVE_LOCK_CHECK (connection); - return _dbus_transport_get_is_connected (connection->transport); -} + /* on OOM error_msg is set to NULL */ + complete_pending_call_and_unlock (connection, pending, error_msg); + dbus_pending_call_unref (pending); + return; + } + else if (tv_sec < start_tv_sec) + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): clock set backward\n"); + else if (connection->disconnect_message_link == NULL) + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): disconnected\n"); + else if (tv_sec < end_tv_sec || + (tv_sec == end_tv_sec && tv_usec < end_tv_usec)) + { + timeout_milliseconds = (end_tv_sec - tv_sec) * 1000 + + (end_tv_usec - tv_usec) / 1000; + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): %d milliseconds remain\n", timeout_milliseconds); + _dbus_assert (timeout_milliseconds >= 0); + + if (status == DBUS_DISPATCH_NEED_MEMORY) + { + /* Try sleeping a bit, as we aren't sure we need to block for reading, + * we may already have a reply in the buffer and just can't process + * it. + */ + _dbus_verbose ("dbus_connection_send_with_reply_and_block() waiting for more memory\n"); + + _dbus_memory_pause_based_on_timeout (timeout_milliseconds); + } + else + { + /* block again, we don't have the reply buffered yet. */ + _dbus_connection_do_iteration_unlocked (connection, + DBUS_ITERATION_DO_READING | + DBUS_ITERATION_BLOCK, + timeout_milliseconds); + } -/** - * Gets whether the connection is currently open. A connection may - * become disconnected when the remote application closes its end, or - * exits; a connection may also be disconnected with - * dbus_connection_close(). - * - * There are not separate states for "closed" and "disconnected," the two - * terms are synonymous. This function should really be called - * get_is_open() but for historical reasons is not. - * - * @param connection the connection. - * @returns #TRUE if the connection is still alive. - */ -dbus_bool_t -dbus_connection_get_is_connected (DBusConnection *connection) -{ - dbus_bool_t res; + goto recheck_status; + } - _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n", + (tv_sec - start_tv_sec) * 1000 + (tv_usec - start_tv_usec) / 1000); + + _dbus_assert (!_dbus_pending_call_get_completed_unlocked (pending)); + /* unlock and call user code */ + complete_pending_call_and_unlock (connection, pending, NULL); + + /* update user code on dispatch status */ CONNECTION_LOCK (connection); - res = _dbus_connection_get_is_connected_unlocked (connection); - CONNECTION_UNLOCK (connection); - - return res; + status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + dbus_pending_call_unref (pending); } +/** @} */ + /** - * Gets whether the connection was authenticated. (Note that - * if the connection was authenticated then disconnected, - * this function still returns #TRUE) + * @addtogroup DBusConnection * - * @param connection the connection - * @returns #TRUE if the connection was ever authenticated + * @{ */ -dbus_bool_t -dbus_connection_get_is_authenticated (DBusConnection *connection) -{ - dbus_bool_t res; - - _dbus_return_val_if_fail (connection != NULL, FALSE); - - CONNECTION_LOCK (connection); - res = _dbus_transport_get_is_authenticated (connection->transport); - CONNECTION_UNLOCK (connection); - - return res; -} /** - * Set whether _exit() should be called when the connection receives a - * disconnect signal. The call to _exit() comes after any handlers for - * the disconnect signal run; handlers can cancel the exit by calling - * this function. + * 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. * - * By default, exit_on_disconnect is #FALSE; but for message bus - * connections returned from dbus_bus_get() it will be toggled on - * by default. + * Use dbus_connection_open_private() to get a dedicated connection + * not shared with other callers of dbus_connection_open(). * - * @param connection the connection - * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal + * 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. + * + * Because this connection is shared, no user of the connection + * may call dbus_connection_close(). However, when you are done with the + * connection you should call dbus_connection_unref(). + * + * @param address the address. + * @param error address where an error can be returned. + * @returns new connection, or #NULL on failure. */ -void -dbus_connection_set_exit_on_disconnect (DBusConnection *connection, - dbus_bool_t exit_on_disconnect) -{ - _dbus_return_if_fail (connection != NULL); - - CONNECTION_LOCK (connection); - connection->exit_on_disconnect = exit_on_disconnect != FALSE; - CONNECTION_UNLOCK (connection); -} - -static DBusPreallocatedSend* -_dbus_connection_preallocate_send_unlocked (DBusConnection *connection) +DBusConnection* +dbus_connection_open (const char *address, + DBusError *error) { - DBusPreallocatedSend *preallocated; - - HAVE_LOCK_CHECK (connection); - - _dbus_assert (connection != NULL); - - preallocated = dbus_new (DBusPreallocatedSend, 1); - if (preallocated == NULL) - return NULL; + DBusConnection *connection; - if (connection->link_cache != NULL) - { - preallocated->queue_link = - _dbus_list_pop_first_link (&connection->link_cache); - preallocated->queue_link->data = NULL; - } - else - { - preallocated->queue_link = _dbus_list_alloc_link (NULL); - if (preallocated->queue_link == NULL) - goto failed_0; - } - - if (connection->link_cache != NULL) - { - preallocated->counter_link = - _dbus_list_pop_first_link (&connection->link_cache); - preallocated->counter_link->data = connection->outgoing_counter; - } - else - { - preallocated->counter_link = _dbus_list_alloc_link (connection->outgoing_counter); - if (preallocated->counter_link == NULL) - goto failed_1; - } + _dbus_return_val_if_fail (address != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); - _dbus_counter_ref (preallocated->counter_link->data); + connection = _dbus_connection_open_internal (address, + TRUE, + error); - preallocated->connection = connection; - - return preallocated; - - failed_1: - _dbus_list_free_link (preallocated->queue_link); - failed_0: - dbus_free (preallocated); - - return NULL; + return connection; } /** - * Preallocates resources needed to send a message, allowing the message - * to be sent without the possibility of memory allocation failure. - * Allows apps to create a future guarantee that they can send - * a message regardless of memory shortages. + * 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. * - * @param connection the connection we're preallocating for. - * @returns the preallocated resources, or #NULL + * 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. + * + * When you are done with this connection, you must + * dbus_connection_close() to disconnect it, + * and dbus_connection_unref() to free the connection object. + * + * (The dbus_connection_close() can be skipped if the + * connection is already known to be disconnected, for example + * if you are inside a handler for the Disconnected signal.) + * + * @param address the address. + * @param error address where an error can be returned. + * @returns new connection, or #NULL on failure. */ -DBusPreallocatedSend* -dbus_connection_preallocate_send (DBusConnection *connection) +DBusConnection* +dbus_connection_open_private (const char *address, + DBusError *error) { - DBusPreallocatedSend *preallocated; - - _dbus_return_val_if_fail (connection != NULL, NULL); + DBusConnection *connection; - CONNECTION_LOCK (connection); - - preallocated = - _dbus_connection_preallocate_send_unlocked (connection); + _dbus_return_val_if_fail (address != NULL, NULL); + _dbus_return_val_if_error_is_set (error, NULL); - CONNECTION_UNLOCK (connection); + connection = _dbus_connection_open_internal (address, + FALSE, + error); - return preallocated; + return connection; } /** - * Frees preallocated message-sending resources from - * dbus_connection_preallocate_send(). Should only - * be called if the preallocated resources are not used - * to send a message. + * Increments the reference count of a DBusConnection. * - * @param connection the connection - * @param preallocated the resources + * @param connection the connection. + * @returns the connection. */ -void -dbus_connection_free_preallocated_send (DBusConnection *connection, - DBusPreallocatedSend *preallocated) +DBusConnection * +dbus_connection_ref (DBusConnection *connection) { - _dbus_return_if_fail (connection != NULL); - _dbus_return_if_fail (preallocated != NULL); - _dbus_return_if_fail (connection == preallocated->connection); + _dbus_return_val_if_fail (connection != NULL, NULL); + _dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ + +#ifdef DBUS_HAVE_ATOMIC_INT + _dbus_atomic_inc (&connection->refcount); +#else + CONNECTION_LOCK (connection); + _dbus_assert (connection->refcount.value > 0); - _dbus_list_free_link (preallocated->queue_link); - _dbus_counter_unref (preallocated->counter_link->data); - _dbus_list_free_link (preallocated->counter_link); - dbus_free (preallocated); + connection->refcount.value += 1; + CONNECTION_UNLOCK (connection); +#endif + + return connection; } -/* Called with lock held, does not update dispatch status */ static void -_dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *connection, - DBusPreallocatedSend *preallocated, - DBusMessage *message, - dbus_uint32_t *client_serial) +free_outgoing_message (void *element, + void *data) { - dbus_uint32_t serial; - const char *sig; + DBusMessage *message = element; + DBusConnection *connection = data; - preallocated->queue_link->data = message; - _dbus_list_prepend_link (&connection->outgoing_messages, - preallocated->queue_link); + _dbus_message_remove_size_counter (message, + connection->outgoing_counter, + NULL); + dbus_message_unref (message); +} - _dbus_message_add_size_counter_link (message, - preallocated->counter_link); +/* This is run without the mutex held, but after the last reference + * to the connection has been dropped we should have no thread-related + * problems + */ +static void +_dbus_connection_last_unref (DBusConnection *connection) +{ + DBusList *link; - dbus_free (preallocated); - preallocated = NULL; + _dbus_verbose ("Finalizing connection %p\n", connection); - dbus_message_ref (message); + _dbus_assert (connection->refcount.value == 0); - connection->n_outgoing += 1; - - sig = dbus_message_get_signature (message); + /* You have to disconnect the connection before unref:ing it. Otherwise + * you won't get the disconnected message. + */ + _dbus_assert (!_dbus_transport_get_is_connected (connection->transport)); + _dbus_assert (connection->server_guid == NULL); - _dbus_verbose ("Message %p (%d %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n", - message, - dbus_message_get_type (message), - dbus_message_get_path (message) ? - dbus_message_get_path (message) : - "no path", - dbus_message_get_interface (message) ? - dbus_message_get_interface (message) : - "no interface", - dbus_message_get_member (message) ? - dbus_message_get_member (message) : - "no member", - sig, - dbus_message_get_destination (message) ? - dbus_message_get_destination (message) : - "null", - connection, - connection->n_outgoing); + /* ---- We're going to call various application callbacks here, hope it doesn't break anything... */ + _dbus_object_tree_free_all_unlocked (connection->objects); + + dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL); + dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL); + dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL); + + _dbus_watch_list_free (connection->watches); + connection->watches = NULL; + + _dbus_timeout_list_free (connection->timeouts); + connection->timeouts = NULL; - if (dbus_message_get_serial (message) == 0) - { - serial = _dbus_connection_get_next_client_serial (connection); - _dbus_message_set_serial (message, serial); - if (client_serial) - *client_serial = serial; - } - else + _dbus_data_slot_list_free (&connection->slot_list); + + link = _dbus_list_get_first_link (&connection->filter_list); + while (link != NULL) { - if (client_serial) - *client_serial = dbus_message_get_serial (message); + DBusMessageFilter *filter = link->data; + DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); + + filter->function = NULL; + _dbus_message_filter_unref (filter); /* calls app callback */ + link->data = NULL; + + link = next; } + _dbus_list_clear (&connection->filter_list); + + /* ---- Done with stuff that invokes application callbacks */ - _dbus_verbose ("Message %p serial is %u\n", - message, dbus_message_get_serial (message)); + _dbus_object_tree_unref (connection->objects); + + _dbus_hash_table_unref (connection->pending_replies); + connection->pending_replies = NULL; - _dbus_message_lock (message); + _dbus_list_clear (&connection->filter_list); + + _dbus_list_foreach (&connection->outgoing_messages, + free_outgoing_message, + connection); + _dbus_list_clear (&connection->outgoing_messages); + + _dbus_list_foreach (&connection->incoming_messages, + (DBusForeachFunction) dbus_message_unref, + NULL); + _dbus_list_clear (&connection->incoming_messages); - /* Now we need to run an iteration to hopefully just write the messages - * out immediately, and otherwise get them queued up - */ - _dbus_connection_do_iteration_unlocked (connection, - DBUS_ITERATION_DO_WRITING, - -1); + _dbus_counter_unref (connection->outgoing_counter); - /* If stuff is still queued up, be sure we wake up the main loop */ - if (connection->n_outgoing > 0) - _dbus_connection_wakeup_mainloop (connection); -} + _dbus_transport_unref (connection->transport); -static void -_dbus_connection_send_preallocated_and_unlock (DBusConnection *connection, - DBusPreallocatedSend *preallocated, - DBusMessage *message, - dbus_uint32_t *client_serial) -{ - DBusDispatchStatus status; + if (connection->disconnect_message_link) + { + DBusMessage *message = connection->disconnect_message_link->data; + dbus_message_unref (message); + _dbus_list_free_link (connection->disconnect_message_link); + } - HAVE_LOCK_CHECK (connection); + _dbus_list_clear (&connection->link_cache); - _dbus_connection_send_preallocated_unlocked_no_update (connection, - preallocated, - message, client_serial); + _dbus_condvar_free_at_location (&connection->dispatch_cond); + _dbus_condvar_free_at_location (&connection->io_path_cond); - _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); - status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_mutex_free_at_location (&connection->io_path_mutex); + _dbus_mutex_free_at_location (&connection->dispatch_mutex); - /* this calls out to user code */ - _dbus_connection_update_dispatch_status_and_unlock (connection, status); + _dbus_mutex_free_at_location (&connection->mutex); + + dbus_free (connection); } /** - * Sends a message using preallocated resources. This function cannot fail. - * It works identically to dbus_connection_send() in other respects. - * Preallocated resources comes from dbus_connection_preallocate_send(). - * This function "consumes" the preallocated resources, they need not - * be freed separately. + * Decrements the reference count of a DBusConnection, and finalizes + * it if the count reaches zero. * - * @param connection the connection - * @param preallocated the preallocated resources - * @param message the message to send - * @param client_serial return location for client serial assigned to the message + * Note: it is a bug to drop the last reference to a connection that + * is still connected. + * + * For shared connections, libdbus will own a reference + * as long as the connection is connected, so you can know that either + * you don't have the last reference, or it's OK to drop the last reference. + * Most connections are shared. dbus_connection_open() and dbus_bus_get() + * return shared connections. + * + * For private connections, the creator of the connection must arrange for + * dbus_connection_close() to be called prior to dropping the last reference. + * Private connections come from dbus_connection_open_private() or dbus_bus_get_private(). + * + * @param connection the connection. */ void -dbus_connection_send_preallocated (DBusConnection *connection, - DBusPreallocatedSend *preallocated, - DBusMessage *message, - dbus_uint32_t *client_serial) +dbus_connection_unref (DBusConnection *connection) { + dbus_bool_t last_unref; + _dbus_return_if_fail (connection != NULL); - _dbus_return_if_fail (preallocated != NULL); - _dbus_return_if_fail (message != NULL); - _dbus_return_if_fail (preallocated->connection == connection); - _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL || - dbus_message_get_member (message) != NULL); - _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL || - (dbus_message_get_interface (message) != NULL && - dbus_message_get_member (message) != NULL)); + _dbus_return_if_fail (connection->generation == _dbus_current_generation); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&connection->refcount) == 1); +#else CONNECTION_LOCK (connection); - _dbus_connection_send_preallocated_and_unlock (connection, - preallocated, - message, client_serial); -} - -static dbus_bool_t -_dbus_connection_send_unlocked_no_update (DBusConnection *connection, - DBusMessage *message, - dbus_uint32_t *client_serial) -{ - DBusPreallocatedSend *preallocated; - - _dbus_assert (connection != NULL); - _dbus_assert (message != NULL); - preallocated = _dbus_connection_preallocate_send_unlocked (connection); - if (preallocated == NULL) - return FALSE; - - _dbus_connection_send_preallocated_unlocked_no_update (connection, - preallocated, - message, - client_serial); - return TRUE; -} - -/** - * Adds a message to the outgoing message queue. Does not block to - * write the message to the network; that happens asynchronously. To - * force the message to be written, call dbus_connection_flush(). - * Because this only queues the message, the only reason it can - * fail is lack of memory. Even if the connection is disconnected, - * no error will be returned. - * - * If the function fails due to lack of memory, it returns #FALSE. - * The function will never fail for other reasons; even if the - * connection is disconnected, you can queue an outgoing message, - * though obviously it won't be sent. - * - * @param connection the connection. - * @param message the message to write. - * @param client_serial return location for client serial. - * @returns #TRUE on success. - */ -dbus_bool_t -dbus_connection_send (DBusConnection *connection, - DBusMessage *message, - dbus_uint32_t *client_serial) -{ - _dbus_return_val_if_fail (connection != NULL, FALSE); - _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_assert (connection->refcount.value > 0); - CONNECTION_LOCK (connection); + connection->refcount.value -= 1; + last_unref = (connection->refcount.value == 0); - return _dbus_connection_send_and_unlock (connection, - message, - client_serial); +#if 0 + printf ("unref() connection %p count = %d\n", connection, connection->refcount.value); +#endif + + CONNECTION_UNLOCK (connection); +#endif + + if (last_unref) + { +#ifndef DBUS_DISABLE_CHECKS + if (_dbus_transport_get_is_connected (connection->transport)) + { + _dbus_warn_check_failed ("The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.\n%s", + connection->shareable ? + "Most likely, the application called unref() too many times and removed a reference belonging to libdbus, since this is a shared connection.\n" : + "Most likely, the application was supposed to call dbus_connection_close(), since this is a private connection.\n"); + return; + } +#endif + _dbus_connection_last_unref (connection); + } } -static dbus_bool_t -reply_handler_timeout (void *data) +/* + * Note that the transport can disconnect itself (other end drops us) + * and in that case this function never runs. So this function must + * not do anything more than disconnect the transport and update the + * dispatch status. + * + * If the transport self-disconnects, then we assume someone will + * dispatch the connection to cause the dispatch status update. + */ +static void +_dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection) { - DBusConnection *connection; DBusDispatchStatus status; - DBusPendingCall *pending = data; - connection = _dbus_pending_call_get_connection_and_lock (pending); + HAVE_LOCK_CHECK (connection); + + _dbus_verbose ("Disconnecting %p\n", connection); - _dbus_pending_call_queue_timeout_error_unlocked (pending, - connection); - _dbus_connection_remove_timeout_unlocked (connection, - _dbus_pending_call_get_timeout_unlocked (pending)); - _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); + /* We need to ref because update_dispatch_status_and_unlock will unref + * the connection if it was shared and libdbus was the only remaining + * refcount holder. + */ + _dbus_connection_ref_unlocked (connection); + + _dbus_transport_disconnect (connection->transport); - _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + /* This has the side effect of queuing the disconnect message link + * (unless we don't have enough memory, possibly, so don't assert it). + * After the disconnect message link is queued, dbus_bus_get/dbus_connection_open + * should never again return the newly-disconnected connection. + * + * However, we only unref the shared connection and exit_on_disconnect when + * the disconnect message reaches the head of the message queue, + * NOT when it's first queued. + */ status = _dbus_connection_get_dispatch_status_unlocked (connection); - /* Unlocks, and calls out to user code */ + /* This calls out to user code */ _dbus_connection_update_dispatch_status_and_unlock (connection, status); - - return TRUE; + + /* Could also call out to user code */ + dbus_connection_unref (connection); } /** - * Queues a message to send, as with dbus_connection_send_message(), - * but also returns a #DBusPendingCall used to receive a reply to the - * message. If no reply is received in the given timeout_milliseconds, - * this function expires the pending reply and generates a synthetic - * error reply (generated in-process, not by the remote application) - * indicating that a timeout occurred. + * Closes a private connection, so no further data can be sent or received. + * This disconnects the transport (such as a socket) underlying the + * connection. * - * A #DBusPendingCall will see a reply message after any filters, but - * before any object instances or other handlers. A #DBusPendingCall - * will always see exactly one reply message, unless it's cancelled - * with dbus_pending_call_cancel(). - * - * If a filter filters out the reply before the handler sees it, the - * reply is immediately timed out and a timeout error reply is - * generated. If a filter removes the timeout error reply then the - * #DBusPendingCall will get confused. Filtering the timeout error - * is thus considered a bug and will print a warning. + * Attempts to send messages after closing a connection are safe, but will result in + * error replies generated locally in libdbus. * - * If #NULL is passed for the pending_return, the #DBusPendingCall - * will still be generated internally, and used to track - * the message reply timeout. This means a timeout error will - * occur if no reply arrives, unlike with dbus_connection_send(). + * This function does not affect the connection's reference count. It's + * safe to close a connection more than once; all calls after the + * first do nothing. It's impossible to "reopen" a connection, a + * new connection must be created. This function may result in a call + * to the DBusDispatchStatusFunction set with + * dbus_connection_set_dispatch_status_function(), as the disconnect + * message it generates needs to be dispatched. * - * 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. + * If a connection is dropped by the remote application, it will + * close itself. * - * @param connection the connection - * @param message the message to send - * @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected - * @param timeout_milliseconds timeout in milliseconds or -1 for default - * @returns #FALSE if no memory, #TRUE otherwise. + * You must close a connection prior to releasing the last reference to + * the connection. If you dbus_connection_unref() for the last time + * without closing the connection, the results are undefined; it + * is a bug in your program and libdbus will try to print a warning. + * + * You may not close a shared connection. Connections created with + * dbus_connection_open() or dbus_bus_get() are shared. + * These connections are owned by libdbus, and applications should + * only unref them, never close them. Applications can know it is + * safe to unref these connections because libdbus will be holding a + * reference as long as the connection is open. Thus, either the + * connection is closed and it is OK to drop the last reference, + * or the connection is open and the app knows it does not have the + * last reference. + * + * Connections created with dbus_connection_open_private() or + * dbus_bus_get_private() are not kept track of or referenced by + * libdbus. The creator of these connections is responsible for + * calling dbus_connection_close() prior to releasing the last + * reference, if the connection is not already disconnected. * + * @param connection the private (unshared) connection to close */ -dbus_bool_t -dbus_connection_send_with_reply (DBusConnection *connection, - DBusMessage *message, - DBusPendingCall **pending_return, - int timeout_milliseconds) +void +dbus_connection_close (DBusConnection *connection) { - DBusPendingCall *pending; - dbus_int32_t serial = -1; - DBusDispatchStatus status; - - _dbus_return_val_if_fail (connection != NULL, FALSE); - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); - - if (pending_return) - *pending_return = NULL; + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (connection->generation == _dbus_current_generation); CONNECTION_LOCK (connection); - if (!_dbus_connection_get_is_connected_unlocked (connection)) - { - CONNECTION_UNLOCK (connection); - - *pending_return = NULL; - - return TRUE; - } - - pending = _dbus_pending_call_new_unlocked (connection, - timeout_milliseconds, - reply_handler_timeout); - - if (pending == NULL) +#ifndef DBUS_DISABLE_CHECKS + if (connection->shareable) { CONNECTION_UNLOCK (connection); - return FALSE; - } - /* Assign a serial to the message */ - serial = dbus_message_get_serial (message); - if (serial == 0) - { - serial = _dbus_connection_get_next_client_serial (connection); - _dbus_message_set_serial (message, serial); + _dbus_warn_check_failed ("Applications must not close shared connections - see dbus_connection_close() docs. This is a bug in the application.\n"); + return; } +#endif + + _dbus_connection_close_possibly_shared_and_unlock (connection); +} - if (!_dbus_pending_call_set_timeout_error_unlocked (pending, message, serial)) - goto error; - - /* Insert the serial in the pending replies hash; - * hash takes a refcount on DBusPendingCall. - * Also, add the timeout. - */ - if (!_dbus_connection_attach_pending_call_unlocked (connection, - pending)) - goto error; - - if (!_dbus_connection_send_unlocked_no_update (connection, message, NULL)) - { - _dbus_connection_detach_pending_call_and_unlock (connection, - pending); - goto error_unlocked; - } +static dbus_bool_t +_dbus_connection_get_is_connected_unlocked (DBusConnection *connection) +{ + HAVE_LOCK_CHECK (connection); + return _dbus_transport_get_is_connected (connection->transport); +} - if (pending_return) - *pending_return = pending; /* hand off refcount */ - else - { - _dbus_connection_detach_pending_call_unlocked (connection, pending); - /* we still have a ref to the pending call in this case, we unref - * after unlocking, below - */ - } +/** + * Gets whether the connection is currently open. A connection may + * become disconnected when the remote application closes its end, or + * exits; a connection may also be disconnected with + * dbus_connection_close(). + * + * There are not separate states for "closed" and "disconnected," the two + * terms are synonymous. This function should really be called + * get_is_open() but for historical reasons is not. + * + * @param connection the connection. + * @returns #TRUE if the connection is still alive. + */ +dbus_bool_t +dbus_connection_get_is_connected (DBusConnection *connection) +{ + dbus_bool_t res; - status = _dbus_connection_get_dispatch_status_unlocked (connection); + _dbus_return_val_if_fail (connection != NULL, FALSE); + + CONNECTION_LOCK (connection); + res = _dbus_connection_get_is_connected_unlocked (connection); + CONNECTION_UNLOCK (connection); + + return res; +} - /* this calls out to user code */ - _dbus_connection_update_dispatch_status_and_unlock (connection, status); +/** + * Gets whether the connection was authenticated. (Note that + * if the connection was authenticated then disconnected, + * this function still returns #TRUE) + * + * @param connection the connection + * @returns #TRUE if the connection was ever authenticated + */ +dbus_bool_t +dbus_connection_get_is_authenticated (DBusConnection *connection) +{ + dbus_bool_t res; - if (pending_return == NULL) - dbus_pending_call_unref (pending); + _dbus_return_val_if_fail (connection != NULL, FALSE); - return TRUE; + CONNECTION_LOCK (connection); + res = _dbus_transport_get_is_authenticated (connection->transport); + CONNECTION_UNLOCK (connection); + + return res; +} + +/** + * Set whether _exit() should be called when the connection receives a + * disconnect signal. The call to _exit() comes after any handlers for + * the disconnect signal run; handlers can cancel the exit by calling + * this function. + * + * By default, exit_on_disconnect is #FALSE; but for message bus + * connections returned from dbus_bus_get() it will be toggled on + * by default. + * + * @param connection the connection + * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal + */ +void +dbus_connection_set_exit_on_disconnect (DBusConnection *connection, + dbus_bool_t exit_on_disconnect) +{ + _dbus_return_if_fail (connection != NULL); - error: + CONNECTION_LOCK (connection); + connection->exit_on_disconnect = exit_on_disconnect != FALSE; CONNECTION_UNLOCK (connection); - error_unlocked: - dbus_pending_call_unref (pending); - return FALSE; } -/* This is slightly strange since we can pop a message here without - * the dispatch lock. +/** + * Preallocates resources needed to send a message, allowing the message + * to be sent without the possibility of memory allocation failure. + * Allows apps to create a future guarantee that they can send + * a message regardless of memory shortages. + * + * @param connection the connection we're preallocating for. + * @returns the preallocated resources, or #NULL */ -static DBusMessage* -check_for_reply_unlocked (DBusConnection *connection, - dbus_uint32_t client_serial) +DBusPreallocatedSend* +dbus_connection_preallocate_send (DBusConnection *connection) { - DBusList *link; + DBusPreallocatedSend *preallocated; - HAVE_LOCK_CHECK (connection); - - link = _dbus_list_get_first_link (&connection->incoming_messages); + _dbus_return_val_if_fail (connection != NULL, NULL); - while (link != NULL) - { - DBusMessage *reply = link->data; + CONNECTION_LOCK (connection); + + preallocated = + _dbus_connection_preallocate_send_unlocked (connection); - if (dbus_message_get_reply_serial (reply) == client_serial) - { - _dbus_list_remove_link (&connection->incoming_messages, link); - connection->n_incoming -= 1; - return reply; - } - link = _dbus_list_get_next_link (&connection->incoming_messages, link); - } + CONNECTION_UNLOCK (connection); - return NULL; + return preallocated; } -static void -connection_timeout_and_complete_all_pending_calls_unlocked (DBusConnection *connection) +/** + * Frees preallocated message-sending resources from + * dbus_connection_preallocate_send(). Should only + * be called if the preallocated resources are not used + * to send a message. + * + * @param connection the connection + * @param preallocated the resources + */ +void +dbus_connection_free_preallocated_send (DBusConnection *connection, + DBusPreallocatedSend *preallocated) { - /* We can't iterate over the hash in the normal way since we'll be - * dropping the lock for each item. So we restart the - * iter each time as we drain the hash table. - */ - - while (_dbus_hash_table_get_n_entries (connection->pending_replies) > 0) - { - DBusPendingCall *pending; - DBusHashIter iter; - - _dbus_hash_iter_init (connection->pending_replies, &iter); - _dbus_hash_iter_next (&iter); - - pending = _dbus_hash_iter_get_value (&iter); - _dbus_pending_call_ref_unlocked (pending); - - _dbus_pending_call_queue_timeout_error_unlocked (pending, - connection); - _dbus_connection_remove_timeout_unlocked (connection, - _dbus_pending_call_get_timeout_unlocked (pending)); - _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); - _dbus_hash_iter_remove_entry (&iter); + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (preallocated != NULL); + _dbus_return_if_fail (connection == preallocated->connection); - _dbus_pending_call_unref_and_unlock (pending); - CONNECTION_LOCK (connection); - } - HAVE_LOCK_CHECK (connection); + _dbus_list_free_link (preallocated->queue_link); + _dbus_counter_unref (preallocated->counter_link->data); + _dbus_list_free_link (preallocated->counter_link); + dbus_free (preallocated); } -static void -complete_pending_call_and_unlock (DBusConnection *connection, - DBusPendingCall *pending, - DBusMessage *message) +/** + * Sends a message using preallocated resources. This function cannot fail. + * It works identically to dbus_connection_send() in other respects. + * Preallocated resources comes from dbus_connection_preallocate_send(). + * This function "consumes" the preallocated resources, they need not + * be freed separately. + * + * @param connection the connection + * @param preallocated the preallocated resources + * @param message the message to send + * @param client_serial return location for client serial assigned to the message + */ +void +dbus_connection_send_preallocated (DBusConnection *connection, + DBusPreallocatedSend *preallocated, + DBusMessage *message, + dbus_uint32_t *client_serial) { - _dbus_pending_call_set_reply_unlocked (pending, message); - _dbus_pending_call_ref_unlocked (pending); /* in case there's no app with a ref held */ - _dbus_connection_detach_pending_call_and_unlock (connection, pending); - - /* Must be called unlocked since it invokes app callback */ - _dbus_pending_call_complete (pending); - dbus_pending_call_unref (pending); + _dbus_return_if_fail (connection != NULL); + _dbus_return_if_fail (preallocated != NULL); + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (preallocated->connection == connection); + _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL || + dbus_message_get_member (message) != NULL); + _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL || + (dbus_message_get_interface (message) != NULL && + dbus_message_get_member (message) != NULL)); + + CONNECTION_LOCK (connection); + _dbus_connection_send_preallocated_and_unlock (connection, + preallocated, + message, client_serial); } static dbus_bool_t -check_for_reply_and_update_dispatch_unlocked (DBusConnection *connection, - DBusPendingCall *pending) +_dbus_connection_send_unlocked_no_update (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial) { - DBusMessage *reply; - DBusDispatchStatus status; - - reply = check_for_reply_unlocked (connection, - _dbus_pending_call_get_reply_serial_unlocked (pending)); - if (reply != NULL) - { - _dbus_verbose ("%s checked for reply\n", _DBUS_FUNCTION_NAME); - - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n"); - - complete_pending_call_and_unlock (connection, pending, reply); - dbus_message_unref (reply); - - CONNECTION_LOCK (connection); - status = _dbus_connection_get_dispatch_status_unlocked (connection); - _dbus_connection_update_dispatch_status_and_unlock (connection, status); - dbus_pending_call_unref (pending); + DBusPreallocatedSend *preallocated; - return TRUE; - } + _dbus_assert (connection != NULL); + _dbus_assert (message != NULL); + + preallocated = _dbus_connection_preallocate_send_unlocked (connection); + if (preallocated == NULL) + return FALSE; - return FALSE; + _dbus_connection_send_preallocated_unlocked_no_update (connection, + preallocated, + message, + client_serial); + return TRUE; } /** - * When a function that blocks has been called with a timeout, and we - * run out of memory, the time to wait for memory is based on the - * timeout. If the caller was willing to block a long time we wait a - * relatively long time for memory, if they were only willing to block - * briefly then we retry for memory at a rapid rate. + * Adds a message to the outgoing message queue. Does not block to + * write the message to the network; that happens asynchronously. To + * force the message to be written, call dbus_connection_flush(). + * Because this only queues the message, the only reason it can + * fail is lack of memory. Even if the connection is disconnected, + * no error will be returned. * - * @timeout_milliseconds the timeout requested for blocking + * If the function fails due to lack of memory, it returns #FALSE. + * The function will never fail for other reasons; even if the + * connection is disconnected, you can queue an outgoing message, + * though obviously it won't be sent. + * + * @param connection the connection. + * @param message the message to write. + * @param client_serial return location for client serial. + * @returns #TRUE on success. */ -static void -_dbus_memory_pause_based_on_timeout (int timeout_milliseconds) -{ - if (timeout_milliseconds == -1) - _dbus_sleep_milliseconds (1000); - else if (timeout_milliseconds < 100) - ; /* just busy loop */ - else if (timeout_milliseconds <= 1000) - _dbus_sleep_milliseconds (timeout_milliseconds / 3); - else - _dbus_sleep_milliseconds (1000); -} - -static DBusMessage * -generate_local_error_message (dbus_uint32_t serial, - char *error_name, - char *error_msg) +dbus_bool_t +dbus_connection_send (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial) { - DBusMessage *message; - message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); - if (!message) - goto out; + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (message != NULL, FALSE); - if (!dbus_message_set_error_name (message, error_name)) - { - dbus_message_unref (message); - message = NULL; - goto out; - } + CONNECTION_LOCK (connection); - dbus_message_set_no_reply (message, TRUE); + return _dbus_connection_send_and_unlock (connection, + message, + client_serial); +} - if (!dbus_message_set_reply_serial (message, - serial)) - { - dbus_message_unref (message); - message = NULL; - goto out; - } +static dbus_bool_t +reply_handler_timeout (void *data) +{ + DBusConnection *connection; + DBusDispatchStatus status; + DBusPendingCall *pending = data; - if (error_msg != NULL) - { - DBusMessageIter iter; + connection = _dbus_pending_call_get_connection_and_lock (pending); - dbus_message_iter_init_append (message, &iter); - if (!dbus_message_iter_append_basic (&iter, - DBUS_TYPE_STRING, - &error_msg)) - { - dbus_message_unref (message); - message = NULL; - goto out; - } - } + _dbus_pending_call_queue_timeout_error_unlocked (pending, + connection); + _dbus_connection_remove_timeout_unlocked (connection, + _dbus_pending_call_get_timeout_unlocked (pending)); + _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE); - out: - return message; + _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME); + status = _dbus_connection_get_dispatch_status_unlocked (connection); + + /* Unlocks, and calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + return TRUE; } /** - * Blocks until a pending call times out or gets a reply. + * Queues a message to send, as with dbus_connection_send_message(), + * but also returns a #DBusPendingCall used to receive a reply to the + * message. If no reply is received in the given timeout_milliseconds, + * this function expires the pending reply and generates a synthetic + * error reply (generated in-process, not by the remote application) + * indicating that a timeout occurred. * - * Does not re-enter the main loop or run filter/path-registered - * callbacks. The reply to the message will not be seen by - * filter callbacks. + * A #DBusPendingCall will see a reply message after any filters, but + * before any object instances or other handlers. A #DBusPendingCall + * will always see exactly one reply message, unless it's cancelled + * with dbus_pending_call_cancel(). + * + * If a filter filters out the reply before the handler sees it, the + * reply is immediately timed out and a timeout error reply is + * generated. If a filter removes the timeout error reply then the + * #DBusPendingCall will get confused. Filtering the timeout error + * is thus considered a bug and will print a warning. + * + * If #NULL is passed for the pending_return, the #DBusPendingCall + * will still be generated internally, and used to track + * the message reply timeout. This means a timeout error will + * occur if no reply arrives, unlike with dbus_connection_send(). * - * Returns immediately if pending call already got a 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. * - * @todo could use performance improvements (it keeps scanning - * the whole message queue for example) + * @param connection the connection + * @param message the message to send + * @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected + * @param timeout_milliseconds timeout in milliseconds or -1 for default + * @returns #FALSE if no memory, #TRUE otherwise. * - * @param pending the pending call we block for a reply on */ -void -_dbus_connection_block_pending_call (DBusPendingCall *pending) +dbus_bool_t +dbus_connection_send_with_reply (DBusConnection *connection, + DBusMessage *message, + DBusPendingCall **pending_return, + int timeout_milliseconds) { - long start_tv_sec, start_tv_usec; - long end_tv_sec, end_tv_usec; - long tv_sec, tv_usec; + DBusPendingCall *pending; + dbus_int32_t serial = -1; DBusDispatchStatus status; - DBusConnection *connection; - dbus_uint32_t client_serial; - int timeout_milliseconds; - - _dbus_assert (pending != NULL); - - if (dbus_pending_call_get_completed (pending)) - return; - - dbus_pending_call_ref (pending); /* necessary because the call could be canceled */ - connection = _dbus_pending_call_get_connection_and_lock (pending); - - /* Flush message queue - note, can affect dispatch status */ - _dbus_connection_flush_unlocked (connection); + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); - client_serial = _dbus_pending_call_get_reply_serial_unlocked (pending); + if (pending_return) + *pending_return = NULL; - /* note that timeout_milliseconds is limited to a smallish value - * in _dbus_pending_call_new() so overflows aren't possible - * below - */ - timeout_milliseconds = dbus_timeout_get_interval (_dbus_pending_call_get_timeout_unlocked (pending)); - - _dbus_get_current_time (&start_tv_sec, &start_tv_usec); - end_tv_sec = start_tv_sec + timeout_milliseconds / 1000; - end_tv_usec = start_tv_usec + (timeout_milliseconds % 1000) * 1000; - end_tv_sec += end_tv_usec / _DBUS_USEC_PER_SECOND; - end_tv_usec = end_tv_usec % _DBUS_USEC_PER_SECOND; + CONNECTION_LOCK (connection); - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %ld sec %ld usec to %ld sec %ld usec\n", - timeout_milliseconds, - client_serial, - start_tv_sec, start_tv_usec, - end_tv_sec, end_tv_usec); + if (!_dbus_connection_get_is_connected_unlocked (connection)) + { + CONNECTION_UNLOCK (connection); - /* check to see if we already got the data off the socket */ - /* from another blocked pending call */ - if (check_for_reply_and_update_dispatch_unlocked (connection, pending)) - return; + *pending_return = NULL; - /* Now we wait... */ - /* always block at least once as we know we don't have the reply yet */ - _dbus_connection_do_iteration_unlocked (connection, - DBUS_ITERATION_DO_READING | - DBUS_ITERATION_BLOCK, - timeout_milliseconds); + return TRUE; + } - recheck_status: + pending = _dbus_pending_call_new_unlocked (connection, + timeout_milliseconds, + reply_handler_timeout); - _dbus_verbose ("%s top of recheck\n", _DBUS_FUNCTION_NAME); - - HAVE_LOCK_CHECK (connection); - - /* queue messages and get status */ + if (pending == NULL) + { + CONNECTION_UNLOCK (connection); + return FALSE; + } - status = _dbus_connection_get_dispatch_status_unlocked (connection); + /* Assign a serial to the message */ + serial = dbus_message_get_serial (message); + if (serial == 0) + { + serial = _dbus_connection_get_next_client_serial (connection); + _dbus_message_set_serial (message, serial); + } - /* the get_completed() is in case a dispatch() while we were blocking - * got the reply instead of us. + if (!_dbus_pending_call_set_timeout_error_unlocked (pending, message, serial)) + goto error; + + /* Insert the serial in the pending replies hash; + * hash takes a refcount on DBusPendingCall. + * Also, add the timeout. */ - if (_dbus_pending_call_get_completed_unlocked (pending)) + if (!_dbus_connection_attach_pending_call_unlocked (connection, + pending)) + goto error; + + if (!_dbus_connection_send_unlocked_no_update (connection, message, NULL)) { - _dbus_verbose ("Pending call completed by dispatch in %s\n", _DBUS_FUNCTION_NAME); - _dbus_connection_update_dispatch_status_and_unlock (connection, status); - dbus_pending_call_unref (pending); - return; + _dbus_connection_detach_pending_call_and_unlock (connection, + pending); + goto error_unlocked; } - - if (status == DBUS_DISPATCH_DATA_REMAINS) { - if (check_for_reply_and_update_dispatch_unlocked (connection, pending)) - return; - } - - _dbus_get_current_time (&tv_sec, &tv_usec); - - if (!_dbus_connection_get_is_connected_unlocked (connection)) - { - DBusMessage *error_msg; - - error_msg = generate_local_error_message (client_serial, - DBUS_ERROR_DISCONNECTED, - "Connection was disconnected before a reply was received"); - /* on OOM error_msg is set to NULL */ - complete_pending_call_and_unlock (connection, pending, error_msg); - dbus_pending_call_unref (pending); - return; - } - else if (tv_sec < start_tv_sec) - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): clock set backward\n"); - else if (connection->disconnect_message_link == NULL) - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): disconnected\n"); - else if (tv_sec < end_tv_sec || - (tv_sec == end_tv_sec && tv_usec < end_tv_usec)) + if (pending_return) + *pending_return = pending; /* hand off refcount */ + else { - timeout_milliseconds = (end_tv_sec - tv_sec) * 1000 + - (end_tv_usec - tv_usec) / 1000; - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): %d milliseconds remain\n", timeout_milliseconds); - _dbus_assert (timeout_milliseconds >= 0); - - if (status == DBUS_DISPATCH_NEED_MEMORY) - { - /* Try sleeping a bit, as we aren't sure we need to block for reading, - * we may already have a reply in the buffer and just can't process - * it. - */ - _dbus_verbose ("dbus_connection_send_with_reply_and_block() waiting for more memory\n"); - - _dbus_memory_pause_based_on_timeout (timeout_milliseconds); - } - else - { - /* block again, we don't have the reply buffered yet. */ - _dbus_connection_do_iteration_unlocked (connection, - DBUS_ITERATION_DO_READING | - DBUS_ITERATION_BLOCK, - timeout_milliseconds); - } - - goto recheck_status; + _dbus_connection_detach_pending_call_unlocked (connection, pending); + /* we still have a ref to the pending call in this case, we unref + * after unlocking, below + */ } - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n", - (tv_sec - start_tv_sec) * 1000 + (tv_usec - start_tv_usec) / 1000); + status = _dbus_connection_get_dispatch_status_unlocked (connection); - _dbus_assert (!_dbus_pending_call_get_completed_unlocked (pending)); + /* this calls out to user code */ + _dbus_connection_update_dispatch_status_and_unlock (connection, status); + + if (pending_return == NULL) + dbus_pending_call_unref (pending); - /* unlock and call user code */ - complete_pending_call_and_unlock (connection, pending, NULL); + return TRUE; - /* update user code on dispatch status */ - CONNECTION_LOCK (connection); - status = _dbus_connection_get_dispatch_status_unlocked (connection); - _dbus_connection_update_dispatch_status_and_unlock (connection, status); + error: + CONNECTION_UNLOCK (connection); + error_unlocked: dbus_pending_call_unref (pending); + return FALSE; } /** -- cgit