D-Bus  1.10.12
dbus-pending-call.c
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-pending-call.c Object representing a call in progress.
00003  *
00004  * Copyright (C) 2002, 2003 Red Hat Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-internals.h"
00026 #include "dbus-connection-internal.h"
00027 #include "dbus-message-internal.h"
00028 #include "dbus-pending-call-internal.h"
00029 #include "dbus-pending-call.h"
00030 #include "dbus-list.h"
00031 #include "dbus-threads.h"
00032 #include "dbus-test.h"
00033 
00053 #define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
00054 
00057 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
00058 
00062 struct DBusPendingCall
00063 {
00064   DBusAtomic refcount;                            
00066   DBusDataSlotList slot_list;                     
00068   DBusPendingCallNotifyFunction function;         
00070   DBusConnection *connection;                     
00071   DBusMessage *reply;                             
00072   DBusTimeout *timeout;                           
00074   DBusList *timeout_link;                         
00076   dbus_uint32_t reply_serial;                     
00078   unsigned int completed : 1;                     
00079   unsigned int timeout_added : 1;                 
00080 };
00081 
00082 static void
00083 _dbus_pending_call_trace_ref (DBusPendingCall *pending_call,
00084     int old_refcount,
00085     int new_refcount,
00086     const char *why)
00087 {
00088 #ifdef DBUS_ENABLE_VERBOSE_MODE
00089   static int enabled = -1;
00090 
00091   _dbus_trace_ref ("DBusPendingCall", pending_call, old_refcount,
00092       new_refcount, why, "DBUS_PENDING_CALL_TRACE", &enabled);
00093 #endif
00094 }
00095 
00096 static dbus_int32_t notify_user_data_slot = -1;
00097 
00108 DBusPendingCall*
00109 _dbus_pending_call_new_unlocked (DBusConnection    *connection,
00110                                  int                timeout_milliseconds,
00111                                  DBusTimeoutHandler timeout_handler)
00112 {
00113   DBusPendingCall *pending;
00114   DBusTimeout *timeout;
00115 
00116   _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
00117  
00118   if (timeout_milliseconds == -1)
00119     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
00120 
00121   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
00122     return NULL;
00123   
00124   pending = dbus_new0 (DBusPendingCall, 1);
00125   
00126   if (pending == NULL)
00127     {
00128       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00129       return NULL;
00130     }
00131 
00132   if (timeout_milliseconds != DBUS_TIMEOUT_INFINITE)
00133     {
00134       timeout = _dbus_timeout_new (timeout_milliseconds,
00135                                    timeout_handler,
00136                                    pending, NULL);  
00137 
00138       if (timeout == NULL)
00139         {
00140           dbus_pending_call_free_data_slot (&notify_user_data_slot);
00141           dbus_free (pending);
00142           return NULL;
00143         }
00144 
00145       pending->timeout = timeout;
00146     }
00147   else
00148     {
00149       pending->timeout = NULL;
00150     }
00151 
00152   _dbus_atomic_inc (&pending->refcount);
00153   pending->connection = connection;
00154   _dbus_connection_ref_unlocked (pending->connection);
00155 
00156   _dbus_data_slot_list_init (&pending->slot_list);
00157 
00158   _dbus_pending_call_trace_ref (pending, 0, 1, "new_unlocked");
00159 
00160   return pending;
00161 }
00162 
00171 void
00172 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
00173                                        DBusMessage     *message)
00174 {
00175   if (message == NULL)
00176     {
00177       message = pending->timeout_link->data;
00178       _dbus_list_clear (&pending->timeout_link);
00179     }
00180   else
00181     dbus_message_ref (message);
00182 
00183   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
00184                  message,
00185                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
00186                  "method return" :
00187                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
00188                  "error" : "other type",
00189                  pending->reply_serial);
00190   
00191   _dbus_assert (pending->reply == NULL);
00192   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
00193   pending->reply = message;
00194 }
00195 
00203 void
00204 _dbus_pending_call_complete (DBusPendingCall *pending)
00205 {
00206   _dbus_assert (!pending->completed);
00207   
00208   pending->completed = TRUE;
00209 
00210   if (pending->function)
00211     {
00212       void *user_data;
00213       user_data = dbus_pending_call_get_data (pending,
00214                                               notify_user_data_slot);
00215       
00216       (* pending->function) (pending, user_data);
00217     }
00218 }
00219 
00227 void
00228 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
00229                                                  DBusConnection  *connection)
00230 {
00231   _dbus_assert (connection == pending->connection);
00232   
00233   if (pending->timeout_link)
00234     {
00235       _dbus_connection_queue_synthesized_message_link (connection,
00236                                                        pending->timeout_link);
00237       pending->timeout_link = NULL;
00238     }
00239 }
00240 
00247 dbus_bool_t 
00248 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
00249 {
00250   _dbus_assert (pending != NULL);
00251 
00252   return pending->timeout_added;
00253 }
00254 
00255 
00262 void
00263 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
00264                                                dbus_bool_t       is_added)
00265 {
00266   _dbus_assert (pending != NULL);
00267 
00268   pending->timeout_added = is_added;
00269 }
00270 
00271 
00278 DBusTimeout *
00279 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
00280 {
00281   _dbus_assert (pending != NULL);
00282 
00283   return pending->timeout;
00284 }
00285 
00292 dbus_uint32_t 
00293 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
00294 {
00295   _dbus_assert (pending != NULL);
00296 
00297   return pending->reply_serial;
00298 }
00299 
00306 void
00307 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
00308                                                dbus_uint32_t serial)
00309 {
00310   _dbus_assert (pending != NULL);
00311   _dbus_assert (pending->reply_serial == 0);
00312 
00313   pending->reply_serial = serial;
00314 }
00315 
00322 DBusConnection *
00323 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
00324 {
00325   _dbus_assert (pending != NULL);
00326  
00327   CONNECTION_LOCK (pending->connection);
00328   return pending->connection;
00329 }
00330 
00337 DBusConnection *
00338 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
00339 {
00340   _dbus_assert (pending != NULL);
00341  
00342   return pending->connection;
00343 }
00344 
00353 dbus_bool_t
00354 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
00355                                                DBusMessage     *message,
00356                                                dbus_uint32_t    serial)
00357 { 
00358   DBusList *reply_link;
00359   DBusMessage *reply;
00360 
00361   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
00362                                   "Did not receive a reply. Possible causes include: "
00363                                   "the remote application did not send a reply, "
00364                                   "the message bus security policy blocked the reply, "
00365                                   "the reply timeout expired, or "
00366                                   "the network connection was broken.");
00367   if (reply == NULL)
00368     return FALSE;
00369 
00370   reply_link = _dbus_list_alloc_link (reply);
00371   if (reply_link == NULL)
00372     {
00373       /* it's OK to unref this, nothing that could have attached a callback
00374        * has ever seen it */
00375       dbus_message_unref (reply);
00376       return FALSE;
00377     }
00378 
00379   pending->timeout_link = reply_link;
00380 
00381   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
00382   
00383   return TRUE;
00384 }
00385 
00393 DBusPendingCall *
00394 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
00395 {
00396   dbus_int32_t old_refcount;
00397 
00398   old_refcount = _dbus_atomic_inc (&pending->refcount);
00399   _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1,
00400       "ref_unlocked");
00401 
00402   return pending;
00403 }
00404 
00405 
00406 static void
00407 _dbus_pending_call_last_unref (DBusPendingCall *pending)
00408 {
00409   DBusConnection *connection;
00410   
00411   /* If we get here, we should be already detached
00412    * from the connection, or never attached.
00413    */
00414   _dbus_assert (!pending->timeout_added);  
00415 
00416   connection = pending->connection;
00417 
00418   /* this assumes we aren't holding connection lock... */
00419   _dbus_data_slot_list_free (&pending->slot_list);
00420 
00421   if (pending->timeout != NULL)
00422     _dbus_timeout_unref (pending->timeout);
00423       
00424   if (pending->timeout_link)
00425     {
00426       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
00427       _dbus_list_free_link (pending->timeout_link);
00428       pending->timeout_link = NULL;
00429     }
00430 
00431   if (pending->reply)
00432     {
00433       dbus_message_unref (pending->reply);
00434       pending->reply = NULL;
00435     }
00436       
00437   dbus_free (pending);
00438 
00439   dbus_pending_call_free_data_slot (&notify_user_data_slot);
00440 
00441   /* connection lock should not be held. */
00442   /* Free the connection last to avoid a weird state while
00443    * calling out to application code where the pending exists
00444    * but not the connection.
00445    */
00446   dbus_connection_unref (connection);
00447 }
00448 
00456 void
00457 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
00458 {
00459   dbus_int32_t old_refcount;
00460 
00461   old_refcount = _dbus_atomic_dec (&pending->refcount);
00462   _dbus_assert (old_refcount > 0);
00463   _dbus_pending_call_trace_ref (pending, old_refcount,
00464       old_refcount - 1, "unref_and_unlock");
00465 
00466   CONNECTION_UNLOCK (pending->connection);
00467 
00468   if (old_refcount == 1)
00469     _dbus_pending_call_last_unref (pending);
00470 }
00471 
00479 dbus_bool_t
00480 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
00481 {
00482   return pending->completed;
00483 }
00484 
00485 static DBusDataSlotAllocator slot_allocator =
00486   _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (pending_call_slots));
00487 
00501 dbus_bool_t
00502 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
00503                                      dbus_int32_t      slot,
00504                                      void             *data,
00505                                      DBusFreeFunction  free_data_func)
00506 {
00507   DBusFreeFunction old_free_func;
00508   void *old_data;
00509   dbus_bool_t retval;
00510 
00511   retval = _dbus_data_slot_list_set (&slot_allocator,
00512                                      &pending->slot_list,
00513                                      slot, data, free_data_func,
00514                                      &old_free_func, &old_data);
00515 
00516   /* Drop locks to call out to app code */
00517   CONNECTION_UNLOCK (pending->connection);
00518   
00519   if (retval)
00520     {
00521       if (old_free_func)
00522         (* old_free_func) (old_data);
00523     }
00524 
00525   CONNECTION_LOCK (pending->connection);
00526   
00527   return retval;
00528 }
00529 
00576 DBusPendingCall *
00577 dbus_pending_call_ref (DBusPendingCall *pending)
00578 {
00579   dbus_int32_t old_refcount;
00580 
00581   _dbus_return_val_if_fail (pending != NULL, NULL);
00582 
00583   old_refcount = _dbus_atomic_inc (&pending->refcount);
00584   _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1,
00585       "ref");
00586 
00587   return pending;
00588 }
00589 
00596 void
00597 dbus_pending_call_unref (DBusPendingCall *pending)
00598 {
00599   dbus_int32_t old_refcount;
00600 
00601   _dbus_return_if_fail (pending != NULL);
00602 
00603   old_refcount = _dbus_atomic_dec (&pending->refcount);
00604   _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount - 1,
00605       "unref");
00606 
00607   if (old_refcount == 1)
00608     _dbus_pending_call_last_unref(pending);
00609 }
00610 
00621 dbus_bool_t
00622 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00623                               DBusPendingCallNotifyFunction function,
00624                               void                         *user_data,
00625                               DBusFreeFunction              free_user_data)
00626 {
00627   dbus_bool_t ret = FALSE;
00628 
00629   _dbus_return_val_if_fail (pending != NULL, FALSE);
00630 
00631   CONNECTION_LOCK (pending->connection);
00632   
00633   /* could invoke application code! */
00634   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00635                                              user_data, free_user_data))
00636     goto out;
00637   
00638   pending->function = function;
00639   ret = TRUE;
00640 
00641 out:
00642   CONNECTION_UNLOCK (pending->connection);
00643   
00644   return ret;
00645 }
00646 
00662 void
00663 dbus_pending_call_cancel (DBusPendingCall *pending)
00664 {
00665   _dbus_return_if_fail (pending != NULL);
00666 
00667   _dbus_connection_remove_pending_call (pending->connection,
00668                                         pending);
00669 }
00670 
00678 dbus_bool_t
00679 dbus_pending_call_get_completed (DBusPendingCall *pending)
00680 {
00681   dbus_bool_t completed;
00682   
00683   _dbus_return_val_if_fail (pending != NULL, FALSE);
00684 
00685   CONNECTION_LOCK (pending->connection);
00686   completed = pending->completed;
00687   CONNECTION_UNLOCK (pending->connection);
00688 
00689   return completed;
00690 }
00691 
00701 DBusMessage*
00702 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00703 {
00704   DBusMessage *message;
00705   
00706   _dbus_return_val_if_fail (pending != NULL, NULL);
00707   _dbus_return_val_if_fail (pending->completed, NULL);
00708   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00709 
00710   CONNECTION_LOCK (pending->connection);
00711   
00712   message = pending->reply;
00713   pending->reply = NULL;
00714 
00715   CONNECTION_UNLOCK (pending->connection);
00716 
00717   _dbus_message_trace_ref (message, -1, -1, "dbus_pending_call_steal_reply");
00718   return message;
00719 }
00720 
00736 void
00737 dbus_pending_call_block (DBusPendingCall *pending)
00738 {
00739   _dbus_return_if_fail (pending != NULL);
00740 
00741   _dbus_connection_block_pending_call (pending);
00742 }
00743 
00758 dbus_bool_t
00759 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00760 {
00761   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00762 
00763   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00764                                           slot_p);
00765 }
00766 
00778 void
00779 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00780 {
00781   _dbus_return_if_fail (slot_p != NULL);
00782   _dbus_return_if_fail (*slot_p >= 0);
00783 
00784   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00785 }
00786 
00800 dbus_bool_t
00801 dbus_pending_call_set_data (DBusPendingCall  *pending,
00802                             dbus_int32_t      slot,
00803                             void             *data,
00804                             DBusFreeFunction  free_data_func)
00805 {
00806   dbus_bool_t retval;
00807   
00808   _dbus_return_val_if_fail (pending != NULL, FALSE);
00809   _dbus_return_val_if_fail (slot >= 0, FALSE);
00810 
00811   
00812   CONNECTION_LOCK (pending->connection);
00813   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00814   CONNECTION_UNLOCK (pending->connection);
00815   return retval;
00816 }
00817 
00826 void*
00827 dbus_pending_call_get_data (DBusPendingCall   *pending,
00828                             dbus_int32_t       slot)
00829 {
00830   void *res;
00831 
00832   _dbus_return_val_if_fail (pending != NULL, NULL);
00833 
00834   CONNECTION_LOCK (pending->connection);
00835   res = _dbus_data_slot_list_get (&slot_allocator,
00836                                   &pending->slot_list,
00837                                   slot);
00838   CONNECTION_UNLOCK (pending->connection);
00839 
00840   return res;
00841 }
00842