From ef614207fc4f03e5cc02faeb109f739eb1ccdf31 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Fri, 15 Aug 2003 04:17:58 +0000 Subject: 2003-08-15 Havoc Pennington * dbus/dbus-connection.c, dbus/dbus-pending-call.c: Finish the pending call stuff --- ChangeLog | 5 + dbus/Makefile.am | 2 + dbus/dbus-connection-internal.h | 45 +++++ dbus/dbus-connection.c | 364 ++++++++++++++++++++++------------------ dbus/dbus-connection.h | 6 +- dbus/dbus-pending-call.c | 146 ++++++++++------ dbus/dbus-pending-call.h | 9 +- dbus/dbus-test.c | 6 + dbus/dbus-test.h | 2 +- dbus/dbus.h | 1 + 10 files changed, 363 insertions(+), 223 deletions(-) diff --git a/ChangeLog b/ChangeLog index e379f432..f1024dbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-08-15 Havoc Pennington + + * dbus/dbus-connection.c, + dbus/dbus-pending-call.c: Finish the pending call stuff + 2003-08-14 Havoc Pennington * dbus/dbus-pending-call.c: start on new object that will replace diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 8c919f31..3537b935 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -19,6 +19,7 @@ dbusinclude_HEADERS= \ dbus-message-handler.h \ dbus-object.h \ dbus-objectid.h \ + dbus-pending-call.h \ dbus-protocol.h \ dbus-server.h \ dbus-threads.h \ @@ -48,6 +49,7 @@ DBUS_LIB_SOURCES= \ dbus-objectid.c \ dbus-object-registry.c \ dbus-object-registry.h \ + dbus-pending-call.c \ dbus-resources.c \ dbus-resources.h \ dbus-server.c \ diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h index 423df5f8..d9d5c5b4 100644 --- a/dbus/dbus-connection-internal.h +++ b/dbus/dbus-connection-internal.h @@ -29,6 +29,7 @@ #include #include #include +#include DBUS_BEGIN_DECLS; @@ -39,6 +40,9 @@ typedef enum DBUS_ITERATION_BLOCK = 1 << 2 /**< Block if nothing to do. */ } DBusIterationFlags; +/** default timeout value when waiting for a message reply */ +#define _DBUS_DEFAULT_TIMEOUT_VALUE (15 * 1000) + void _dbus_connection_lock (DBusConnection *connection); void _dbus_connection_unlock (DBusConnection *connection); void _dbus_connection_ref_unlocked (DBusConnection *connection); @@ -85,6 +89,47 @@ DBusHandlerResult _dbus_message_handler_handle_message (DBusMessageHandl void _dbus_connection_init_id (DBusConnection *connection, DBusObjectID *id); +DBusPendingCall* _dbus_pending_call_new (DBusConnection *connection, + int timeout_milliseconds, + DBusTimeoutHandler timeout_handler); + +void _dbus_pending_call_notify (DBusPendingCall *pending); + +void _dbus_connection_remove_pending_call (DBusConnection *connection, + DBusPendingCall *pending); + +/** + * @addtogroup DBusPendingCallInternals DBusPendingCall implementation details + * @{ + */ +/** + * @brief Internals of DBusPendingCall + * + * Object representing a reply message that we're waiting for. + */ +struct DBusPendingCall +{ + DBusAtomic refcount; /**< reference count */ + + DBusPendingCallNotifyFunction function; /**< Notifier when reply arrives. */ + void *user_data; /**< user data for function */ + DBusFreeFunction free_user_data; /**< free the user data */ + + DBusConnection *connection; /**< Connections we're associated with */ + DBusMessage *reply; /**< Reply (after we've received it) */ + DBusTimeout *timeout; /**< Timeout */ + + DBusList *timeout_link; /**< Preallocated timeout response */ + + dbus_uint32_t reply_serial; /**< Expected serial of reply */ + + unsigned int completed : 1; /**< TRUE if completed */ + unsigned int timeout_added : 1; /**< Have added the timeout */ +}; + +/** @} End of DBusPendingCallInternals */ + + DBUS_END_DECLS; #endif /* DBUS_CONNECTION_INTERNAL_H */ diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index ba5601e3..3af00ed0 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -37,6 +37,7 @@ #include "dbus-dataslot.h" #include "dbus-object-registry.h" #include "dbus-string.h" +#include "dbus-pending-call.h" #if 0 #define CONNECTION_LOCK(connection) do { \ @@ -124,9 +125,6 @@ * @{ */ -/** default timeout value when waiting for a message reply */ -#define DEFAULT_TIMEOUT_VALUE (15 * 1000) - static dbus_bool_t _dbus_modify_sigpipe = TRUE; /** @@ -163,7 +161,7 @@ struct DBusConnection DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ - DBusHashTable *pending_replies; /**< Hash of message serials and their message handlers. */ + DBusHashTable *pending_replies; /**< Hash of message serials to #DBusPendingCall. */ dbus_uint32_t client_serial; /**< Client serial. Increments each time a message is sent */ DBusList *disconnect_message_link; /**< Preallocated list node for queueing the disconnection message */ @@ -184,21 +182,6 @@ struct DBusConnection DBusObjectRegistry *objects; /**< Objects registered with this connection */ }; -typedef struct -{ - DBusConnection *connection; - DBusMessageHandler *handler; - DBusTimeout *timeout; - int serial; - - DBusList *timeout_link; /* Preallocated timeout response */ - - dbus_bool_t timeout_added; - dbus_bool_t connection_added; -} ReplyHandlerData; - -static void reply_handler_data_free (ReplyHandlerData *data); - static void _dbus_connection_remove_timeout_locked (DBusConnection *connection, DBusTimeout *timeout); static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection); @@ -283,7 +266,7 @@ void _dbus_connection_queue_received_message_link (DBusConnection *connection, DBusList *link) { - ReplyHandlerData *reply_handler_data; + DBusPendingCall *pending; dbus_int32_t reply_serial; DBusMessage *message; @@ -297,14 +280,15 @@ _dbus_connection_queue_received_message_link (DBusConnection *connection, reply_serial = dbus_message_get_reply_serial (message); if (reply_serial != -1) { - reply_handler_data = _dbus_hash_table_lookup_int (connection->pending_replies, - reply_serial); - if (reply_handler_data != NULL) + pending = _dbus_hash_table_lookup_int (connection->pending_replies, + reply_serial); + if (pending != NULL) { - if (reply_handler_data->timeout_added) + if (pending->timeout_added) _dbus_connection_remove_timeout_locked (connection, - reply_handler_data->timeout); - reply_handler_data->timeout_added = FALSE; + pending->timeout); + + pending->timeout_added = FALSE; } } @@ -555,6 +539,86 @@ _dbus_connection_notify_disconnected (DBusConnection *connection) } } +static dbus_bool_t +_dbus_connection_attach_pending_call_unlocked (DBusConnection *connection, + DBusPendingCall *pending) +{ + _dbus_assert (pending->reply_serial != 0); + + if (!_dbus_connection_add_timeout (connection, pending->timeout)) + return FALSE; + + if (!_dbus_hash_table_insert_int (connection->pending_replies, + pending->reply_serial, + pending)) + { + _dbus_connection_remove_timeout (connection, pending->timeout); + return FALSE; + } + + pending->timeout_added = TRUE; + pending->connection = connection; + + dbus_pending_call_ref (pending); + + return TRUE; +} + +static void +free_pending_call_on_hash_removal (void *data) +{ + DBusPendingCall *pending; + + if (data == NULL) + return; + + pending = data; + + if (pending->connection) + { + if (pending->timeout_added) + { + _dbus_connection_remove_timeout (pending->connection, + pending->timeout); + pending->timeout_added = FALSE; + } + + pending->connection = NULL; + + dbus_pending_call_unref (pending); + } +} + +static void +_dbus_connection_detach_pending_call_and_unlock (DBusConnection *connection, + DBusPendingCall *pending) +{ + /* The idea here is to avoid finalizing the pending call + * with the lock held, since there's a destroy notifier + * in pending call that goes out to application code. + */ + dbus_pending_call_ref (pending); + _dbus_hash_table_remove_int (connection->pending_replies, + pending->reply_serial); + CONNECTION_UNLOCK (connection); + dbus_pending_call_unref (pending); +} + +/** + * Removes a pending call from the connection, such that + * the pending reply will be ignored. May drop the last + * reference to the pending call. + * + * @param connection the connection + * @param pending the pending call + */ +void +_dbus_connection_remove_pending_call (DBusConnection *connection, + DBusPendingCall *pending) +{ + CONNECTION_LOCK (connection); + _dbus_connection_detach_pending_call_and_unlock (connection, pending); +} /** * Acquire the transporter I/O path. This must be done before @@ -699,7 +763,8 @@ _dbus_connection_new_for_transport (DBusTransport *transport) pending_replies = _dbus_hash_table_new (DBUS_HASH_INT, - NULL, (DBusFreeFunction)reply_handler_data_free); + NULL, + (DBusFreeFunction)free_pending_call_on_hash_removal); if (pending_replies == NULL) goto error; @@ -1442,6 +1507,28 @@ dbus_connection_send_preallocated (DBusConnection *connection, CONNECTION_UNLOCK (connection); } +static dbus_bool_t +_dbus_connection_send_unlocked (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 (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 @@ -1465,50 +1552,41 @@ dbus_connection_send (DBusConnection *connection, DBusMessage *message, dbus_uint32_t *client_serial) { - DBusPreallocatedSend *preallocated; - _dbus_return_val_if_fail (connection != NULL, FALSE); _dbus_return_val_if_fail (message != NULL, FALSE); CONNECTION_LOCK (connection); - - preallocated = _dbus_connection_preallocate_send_unlocked (connection); - if (preallocated == NULL) + + if (!_dbus_connection_send_unlocked (connection, message, client_serial)) { CONNECTION_UNLOCK (connection); return FALSE; } - else - { - _dbus_connection_send_preallocated_unlocked (connection, - preallocated, - message, - client_serial); - CONNECTION_UNLOCK (connection); - return TRUE; - } + + CONNECTION_UNLOCK (connection); + return TRUE; } static dbus_bool_t reply_handler_timeout (void *data) { DBusConnection *connection; - ReplyHandlerData *reply_handler_data = data; DBusDispatchStatus status; + DBusPendingCall *pending = data; - connection = reply_handler_data->connection; + connection = pending->connection; CONNECTION_LOCK (connection); - if (reply_handler_data->timeout_link) + if (pending->timeout_link) { _dbus_connection_queue_synthesized_message_link (connection, - reply_handler_data->timeout_link); - reply_handler_data->timeout_link = NULL; + pending->timeout_link); + pending->timeout_link = NULL; } _dbus_connection_remove_timeout (connection, - reply_handler_data->timeout); - reply_handler_data->timeout_added = FALSE; + pending->timeout); + pending->timeout_added = FALSE; status = _dbus_connection_get_dispatch_status_unlocked (connection); @@ -1518,52 +1596,29 @@ reply_handler_timeout (void *data) return TRUE; } -static void -reply_handler_data_free (ReplyHandlerData *data) -{ - if (!data) - return; - - if (data->timeout_added) - _dbus_connection_remove_timeout_locked (data->connection, - data->timeout); - - if (data->connection_added) - _dbus_message_handler_remove_connection (data->handler, - data->connection); - - if (data->timeout_link) - { - dbus_message_unref ((DBusMessage *)data->timeout_link->data); - _dbus_list_free_link (data->timeout_link); - } - - dbus_message_handler_unref (data->handler); - - dbus_free (data); -} - /** * Queues a message to send, as with dbus_connection_send_message(), - * but also sets up a DBusMessageHandler to receive a reply to the + * but also returns a #DBusPendingCall used 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 one reply has been seen the handler is - * removed. If a filter filters out the reply before the handler sees - * it, the reply is immediately timed out and a timeout error reply is + * 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. + * + * 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 - * reply handler will never be called. Filters should not do this. + * #DBusPendingCall will get confused. Filtering the timeout error + * is thus considered a bug and will print a warning. * - * 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 #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(). * * 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 @@ -1573,7 +1628,7 @@ reply_handler_data_free (ReplyHandlerData *data) * * @param connection the connection * @param message the message to send - * @param reply_handler message handler expecting the reply, or #NULL + * @param pending_return return location for a #DBusPendingCall object, or #NULL * @param timeout_milliseconds timeout in milliseconds or -1 for default * @returns #TRUE if the message is successfully queued, #FALSE if no memory. * @@ -1581,63 +1636,30 @@ reply_handler_data_free (ReplyHandlerData *data) dbus_bool_t dbus_connection_send_with_reply (DBusConnection *connection, DBusMessage *message, - DBusMessageHandler *reply_handler, + DBusPendingCall **pending_return, int timeout_milliseconds) { - DBusTimeout *timeout; - ReplyHandlerData *data; + DBusPendingCall *pending; DBusMessage *reply; DBusList *reply_link; dbus_int32_t serial = -1; _dbus_return_val_if_fail (connection != NULL, FALSE); _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (reply_handler != NULL, FALSE); _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); - - if (timeout_milliseconds == -1) - timeout_milliseconds = DEFAULT_TIMEOUT_VALUE; - data = dbus_new0 (ReplyHandlerData, 1); - - if (!data) - return FALSE; + if (pending_return) + *pending_return = NULL; - timeout = _dbus_timeout_new (timeout_milliseconds, reply_handler_timeout, - data, NULL); + pending = _dbus_pending_call_new (connection, + timeout_milliseconds, + reply_handler_timeout); - if (!timeout) - { - reply_handler_data_free (data); - return FALSE; - } + if (pending == NULL) + return FALSE; CONNECTION_LOCK (connection); - /* Add timeout */ - if (!_dbus_connection_add_timeout (connection, timeout)) - { - reply_handler_data_free (data); - _dbus_timeout_unref (timeout); - CONNECTION_UNLOCK (connection); - return FALSE; - } - - /* The connection now owns the reference to the timeout. */ - _dbus_timeout_unref (timeout); - - data->timeout_added = TRUE; - data->timeout = timeout; - data->connection = connection; - - if (!_dbus_message_handler_add_connection (reply_handler, connection)) - { - CONNECTION_UNLOCK (connection); - reply_handler_data_free (data); - return FALSE; - } - data->connection_added = TRUE; - /* Assign a serial to the message */ if (dbus_message_get_serial (message) == 0) { @@ -1645,17 +1667,14 @@ dbus_connection_send_with_reply (DBusConnection *connection, _dbus_message_set_serial (message, serial); } - data->handler = reply_handler; - data->serial = serial; - - dbus_message_handler_ref (reply_handler); + pending->reply_serial = serial; reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY, "No reply within specified time"); if (!reply) { CONNECTION_UNLOCK (connection); - reply_handler_data_free (data); + dbus_pending_call_unref (pending); return FALSE; } @@ -1664,33 +1683,42 @@ dbus_connection_send_with_reply (DBusConnection *connection, { CONNECTION_UNLOCK (connection); dbus_message_unref (reply); - reply_handler_data_free (data); + dbus_pending_call_unref (pending); return FALSE; } - data->timeout_link = reply_link; - - /* Insert the serial in the pending replies hash. */ - if (!_dbus_hash_table_insert_int (connection->pending_replies, serial, data)) + pending->timeout_link = reply_link; + + /* 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)) { CONNECTION_UNLOCK (connection); - reply_handler_data_free (data); + dbus_pending_call_unref (pending); return FALSE; } - - CONNECTION_UNLOCK (connection); - if (!dbus_connection_send (connection, message, NULL)) + if (!_dbus_connection_send_unlocked (connection, message, NULL)) { - /* This will free the handler data too */ - _dbus_hash_table_remove_int (connection->pending_replies, serial); + _dbus_connection_detach_pending_call_and_unlock (connection, + pending); return FALSE; } + if (pending_return) + { + dbus_pending_call_ref (pending); + *pending_return = pending; + } + + CONNECTION_UNLOCK (connection); + return TRUE; } - static DBusMessage* check_for_reply_unlocked (DBusConnection *connection, dbus_uint32_t client_serial) @@ -1755,7 +1783,7 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection, _dbus_return_val_if_error_is_set (error, NULL); if (timeout_milliseconds == -1) - timeout_milliseconds = DEFAULT_TIMEOUT_VALUE; + timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE; /* it would probably seem logical to pass in _DBUS_INT_MAX * for infinite timeout, but then math below would get @@ -2283,7 +2311,7 @@ dbus_connection_dispatch (DBusConnection *connection) DBusMessage *message; DBusList *link, *filter_list_copy, *message_link; DBusHandlerResult result; - ReplyHandlerData *reply_handler_data; + DBusPendingCall *pending; dbus_int32_t reply_serial; DBusDispatchStatus status; @@ -2332,8 +2360,8 @@ dbus_connection_dispatch (DBusConnection *connection) result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; reply_serial = dbus_message_get_reply_serial (message); - reply_handler_data = _dbus_hash_table_lookup_int (connection->pending_replies, - reply_serial); + pending = _dbus_hash_table_lookup_int (connection->pending_replies, + reply_serial); if (!_dbus_list_copy (&connection->filter_list, &filter_list_copy)) { @@ -2386,34 +2414,42 @@ dbus_connection_dispatch (DBusConnection *connection) goto out; /* Did a reply we were waiting on get filtered? */ - if (reply_handler_data && result == DBUS_HANDLER_RESULT_HANDLED) + if (pending && result == DBUS_HANDLER_RESULT_HANDLED) { /* Queue the timeout immediately! */ - if (reply_handler_data->timeout_link) + if (pending->timeout_link) { _dbus_connection_queue_synthesized_message_link (connection, - reply_handler_data->timeout_link); - reply_handler_data->timeout_link = NULL; + pending->timeout_link); + pending->timeout_link = NULL; } else { /* We already queued the timeout? Then it was filtered! */ - _dbus_warn ("The timeout error with reply serial %d was filtered, so the reply handler will never be called.\n", reply_serial); + _dbus_warn ("The timeout error with reply serial %d was filtered, so the DBusPendingCall will never stop pending.\n", reply_serial); } } if (result == DBUS_HANDLER_RESULT_HANDLED) goto out; - if (reply_handler_data) + if (pending) { - CONNECTION_UNLOCK (connection); + _dbus_verbose (" handing message %p to pending call\n", message); + + _dbus_assert (pending->reply == NULL); + pending->reply = message; + dbus_message_ref (pending->reply); + + dbus_pending_call_ref (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_notify (pending); + dbus_pending_call_unref (pending); - _dbus_verbose (" running reply handler on message %p\n", message); + pending = NULL; - result = _dbus_message_handler_handle_message (reply_handler_data->handler, - connection, message); - reply_handler_data_free (reply_handler_data); CONNECTION_LOCK (connection); goto out; } diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index 7bf1221a..ef106524 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -37,6 +37,7 @@ typedef struct DBusWatch DBusWatch; typedef struct DBusTimeout DBusTimeout; typedef struct DBusMessageHandler DBusMessageHandler; typedef struct DBusPreallocatedSend DBusPreallocatedSend; +typedef struct DBusPendingCall DBusPendingCall; typedef enum { @@ -76,6 +77,9 @@ typedef dbus_bool_t (* DBusAllowUnixUserFunction) (DBusConnection *connection, unsigned long uid, void *data); +typedef void (* DBusPendingCallNotifyFunction) (DBusPendingCall *pending, + void *user_data); + DBusConnection* dbus_connection_open (const char *address, DBusError *error); void dbus_connection_ref (DBusConnection *connection); @@ -97,7 +101,7 @@ dbus_bool_t dbus_connection_send (DBusConnection dbus_uint32_t *client_serial); dbus_bool_t dbus_connection_send_with_reply (DBusConnection *connection, DBusMessage *message, - DBusMessageHandler *reply_handler, + DBusPendingCall **pending_return, int timeout_milliseconds); DBusMessage * dbus_connection_send_with_reply_and_block (DBusConnection *connection, DBusMessage *message, diff --git a/dbus/dbus-pending-call.c b/dbus/dbus-pending-call.c index 7a65ad62..84ca7ae0 100644 --- a/dbus/dbus-pending-call.c +++ b/dbus/dbus-pending-call.c @@ -22,11 +22,11 @@ */ #include "dbus-internals.h" -#include "dbus-message-pending.h" +#include "dbus-connection-internal.h" +#include "dbus-pending-call.h" #include "dbus-list.h" #include "dbus-threads.h" #include "dbus-test.h" -#include "dbus-connection-internal.h" /** * @defgroup DBusPendingCallInternals DBusPendingCall implementation details @@ -38,56 +38,65 @@ * @{ */ -/** - * @brief Internals of DBusPendingCall - * - * Object representing a reply message that we're waiting for. - */ -struct DBusPendingCall -{ - DBusAtomic refcount; /**< reference count */ - - DBusPendingCallNotifyFunction function; /**< Notifier when reply arrives. */ - void *user_data; /**< user data for function */ - DBusFreeFunction free_user_data; /**< free the user data */ - - DBusConnection *connection; /**< Connections we're associated with */ - DBusMessage *reply; /**< Reply (after we've received it) */ - DBusTimeout *timeout; /**< Timeout */ - - DBusList *timeout_link; /**< Preallocated timeout response */ - - dbus_uint32_t reply_serial; /**< Expected serial of reply */ - - unsigned int completed : 1; /**< TRUE if completed */ - unsigned int timeout_added : 1; /**< Have added the timeout */ -}; - /** * Creates a new pending reply object. * * @param connection connection where reply will arrive - * @param reply_serial reply serial of the expected reply + * @param timeout_milliseconds length of timeout, -1 for default + * @param timeout_handler timeout handler, takes pending call as data * @returns a new #DBusPendingCall or #NULL if no memory. */ DBusPendingCall* -_dbus_pending_call_new (DBusConnection *connection, - dbus_uint32_t reply_serial) +_dbus_pending_call_new (DBusConnection *connection, + int timeout_milliseconds, + DBusTimeoutHandler timeout_handler) { DBusPendingCall *pending; + DBusTimeout *timeout; + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); + + if (timeout_milliseconds == -1) + timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE; + pending = dbus_new (DBusPendingCall, 1); if (pending == NULL) return NULL; + timeout = _dbus_timeout_new (timeout_milliseconds, + timeout_handler, + pending, NULL); + + if (timeout == NULL) + { + dbus_free (pending); + return NULL; + } + pending->refcount.value = 1; pending->connection = connection; - pending->reply_serial = reply_serial; - + pending->timeout = timeout; + return pending; } +/** + * Calls notifier function for the pending call + * and sets the call to completed. + * + * @param pending the pending call + * + */ +void +_dbus_pending_call_notify (DBusPendingCall *pending) +{ + pending->completed = TRUE; + + if (pending->function) + (* pending->function) (pending, pending->user_data); +} + /** @} */ /** @@ -138,14 +147,24 @@ dbus_pending_call_unref (DBusPendingCall *pending) if (last_unref) { + /* If we get here, we should be already detached + * from the connection, or never attached. + */ + _dbus_assert (pending->connection == NULL); + _dbus_assert (!pending->timeout_added); + + /* this assumes we aren't holding connection lock... */ if (pending->free_user_data) (* pending->free_user_data) (pending->user_data); - - if (pending->connection != NULL) + if (pending->timeout != NULL) + _dbus_timeout_unref (pending->timeout); + + if (pending->timeout_link) { - _dbus_connection_pending_destroyed_locked (connection, pending); - pending->connection = NULL; + dbus_message_unref ((DBusMessage *)pending->timeout_link->data); + _dbus_list_free_link (pending->timeout_link); + pending->timeout_link = NULL; } if (pending->reply) @@ -179,37 +198,66 @@ dbus_pending_call_set_notify (DBusPendingCall *pending, _dbus_return_if_fail (pending != NULL); - _DBUS_LOCK (pending_call); old_free_func = pending->free_user_data; old_user_data = pending->user_data; pending->user_data = user_data; pending->free_user_data = free_user_data; pending->function = function; - _DBUS_UNLOCK (pending_call); if (old_free_func) (* old_free_func) (old_user_data); } -/** @} */ +/** + * Cancels the pending call, such that any reply + * or error received will just be ignored. + * Drops at least one reference to the #DBusPendingCall + * so will free the call if nobody else is holding + * a reference. + * + * @param pending the pending call + */ +void +dbus_pending_call_cancel (DBusPendingCall *pending) +{ + if (pending->connection) + _dbus_connection_remove_pending_call (pending->connection, + pending); +} -#ifdef DBUS_BUILD_TESTS -static DBusPendingResult -test_pending (DBusPendingCall *pending, - DBusConnection *connection, - DBusMessage *message, - void *user_data) +/** + * Checks whether the pending call has received a reply + * yet, or not. + * + * @param pending the pending call + * @returns #TRUE if a reply has been received + */ +dbus_bool_t +dbus_pending_call_get_completed (DBusPendingCall *pending) { - return DBUS_PENDING_RESULT_NOT_YET_HANDLED; + return pending->completed; } -static void -free_test_data (void *data) +/** + * Gets the reply, or returns #NULL if none has been received yet. The + * reference count is not incremented on the returned message, so you + * have to keep a reference count on the pending call (or add one + * to the message). + * + * @param pending the pending call + * @returns the reply message or #NULL. + */ +DBusMessage* +dbus_pending_call_get_reply (DBusPendingCall *pending) { - /* does nothing */ + return pending->reply; } +/** @} */ + +#ifdef DBUS_BUILD_TESTS + /** * @ingroup DBusPendingCallInternals * Unit test for DBusPendingCall. diff --git a/dbus/dbus-pending-call.h b/dbus/dbus-pending-call.h index ff2c176a..66f1bac5 100644 --- a/dbus/dbus-pending-call.h +++ b/dbus/dbus-pending-call.h @@ -33,23 +33,16 @@ DBUS_BEGIN_DECLS; -typedef void (* DBusPendingCallNotifyFunction) (DBusPendingCall *pending, - void *user_data); - -DBusPendingCall* _dbus_pending_call_new (DBusConnection *connection, - dbus_uint32_t reply_serial); - void dbus_pending_call_ref (DBusPendingCall *pending); void dbus_pending_call_unref (DBusPendingCall *pending); void dbus_pending_call_set_notify (DBusPendingCall *pending, DBusPendingCallNotifyFunction function, void *user_data, DBusFreeFunction free_user_data); +void dbus_pending_call_cancel (DBusPendingCall *pending); dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall *pending); DBusMessage* dbus_pending_call_get_reply (DBusPendingCall *pending); - - DBUS_END_DECLS; #endif /* DBUS_PENDING_CALL_H */ diff --git a/dbus/dbus-test.c b/dbus/dbus-test.c index c3b31107..8a99d179 100644 --- a/dbus/dbus-test.c +++ b/dbus/dbus-test.c @@ -197,6 +197,12 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir) die ("auth"); check_memleaks (); + + printf ("%s: running pending call tests\n", "dbus-test"); + if (!_dbus_pending_call_test (test_data_dir)) + die ("auth"); + + check_memleaks (); printf ("%s: completed successfully\n", "dbus-test"); #else diff --git a/dbus/dbus-test.h b/dbus/dbus-test.h index 8537be40..276e8f9e 100644 --- a/dbus/dbus-test.h +++ b/dbus/dbus-test.h @@ -56,7 +56,7 @@ dbus_bool_t _dbus_memory_test (void); dbus_bool_t _dbus_object_test (void); dbus_bool_t _dbus_object_id_test (void); dbus_bool_t _dbus_object_registry_test (void); - +dbus_bool_t _dbus_pending_call_test (const char *test_data_dir); void dbus_internal_do_not_use_run_tests (const char *test_data_dir); dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString *filename, diff --git a/dbus/dbus.h b/dbus/dbus.h index d83a4a50..12a087f5 100644 --- a/dbus/dbus.h +++ b/dbus/dbus.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.1