2003-08-15 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Fri, 15 Aug 2003 04:17:58 +0000 (04:17 +0000)
committerHavoc Pennington <hp@redhat.com>
Fri, 15 Aug 2003 04:17:58 +0000 (04:17 +0000)
* dbus/dbus-connection.c,
dbus/dbus-pending-call.c: Finish the pending call stuff

ChangeLog
dbus/Makefile.am
dbus/dbus-connection-internal.h
dbus/dbus-connection.c
dbus/dbus-connection.h
dbus/dbus-pending-call.c
dbus/dbus-pending-call.h
dbus/dbus-test.c
dbus/dbus-test.h
dbus/dbus.h

index e379f43..f1024db 100644 (file)
--- 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
index 8c919f3..3537b93 100644 (file)
@@ -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                           \
index 423df5f..d9d5c5b 100644 (file)
@@ -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 */
index ba5601e..3af00ed 100644 (file)
@@ -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 {                      \
  * @{
  */
 
-/** 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;
     }
index 7bf1221..ef10652 100644 (file)
@@ -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,
index 7a65ad6..84ca7ae 100644 (file)
  */
 
 #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
  */
 
 /**
- * @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.
index ff2c176..66f1bac 100644 (file)
 
 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 */
index c3b3110..8a99d17 100644 (file)
@@ -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
index 8537be4..276e8f9 100644 (file)
@@ -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,
index d83a4a5..12a087f 100644 (file)
@@ -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>