summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-08-15 04:17:58 +0000
committerHavoc Pennington <hp@redhat.com>2003-08-15 04:17:58 +0000
commitef614207fc4f03e5cc02faeb109f739eb1ccdf31 (patch)
tree8e32a70a0094c6605c29f2f2ed99075b6fc0d483
parenta6c8a71b1bcba04b63812a61f668e87af0922e5e (diff)
2003-08-15 Havoc Pennington <hp@pobox.com>
* dbus/dbus-connection.c, dbus/dbus-pending-call.c: Finish the pending call stuff
-rw-r--r--ChangeLog5
-rw-r--r--dbus/Makefile.am2
-rw-r--r--dbus/dbus-connection-internal.h45
-rw-r--r--dbus/dbus-connection.c364
-rw-r--r--dbus/dbus-connection.h6
-rw-r--r--dbus/dbus-pending-call.c146
-rw-r--r--dbus/dbus-pending-call.h9
-rw-r--r--dbus/dbus-test.c6
-rw-r--r--dbus/dbus-test.h2
-rw-r--r--dbus/dbus.h1
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 <hp@pobox.com>
+
+ * dbus/dbus-connection.c,
+ dbus/dbus-pending-call.c: Finish the pending call stuff
+
2003-08-14 Havoc Pennington <hp@redhat.com>
* 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 <dbus/dbus-transport.h>
#include <dbus/dbus-resources.h>
#include <dbus/dbus-list.h>
+#include <dbus/dbus-timeout.h>
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
@@ -39,55 +39,64 @@
*/
/**
- * @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 <dbus/dbus-message-handler.h>
#include <dbus/dbus-object.h>
#include <dbus/dbus-objectid.h>
+#include <dbus/dbus-pending-call.h>
#include <dbus/dbus-protocol.h>
#include <dbus/dbus-server.h>
#include <dbus/dbus-threads.h>