• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List

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 "dbus-internals.h"
00025 #include "dbus-connection-internal.h"
00026 #include "dbus-pending-call-internal.h"
00027 #include "dbus-pending-call.h"
00028 #include "dbus-list.h"
00029 #include "dbus-threads.h"
00030 #include "dbus-test.h"
00031 
00051 #define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
00052 
00055 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
00056 
00060 struct DBusPendingCall
00061 {
00062   DBusAtomic refcount;                            
00064   DBusDataSlotList slot_list;                     
00066   DBusPendingCallNotifyFunction function;         
00068   DBusConnection *connection;                     
00069   DBusMessage *reply;                             
00070   DBusTimeout *timeout;                           
00072   DBusList *timeout_link;                         
00074   dbus_uint32_t reply_serial;                     
00076   unsigned int completed : 1;                     
00077   unsigned int timeout_added : 1;                 
00078 };
00079 
00080 static dbus_int32_t notify_user_data_slot = -1;
00081 
00090 DBusPendingCall*
00091 _dbus_pending_call_new_unlocked (DBusConnection    *connection,
00092                                  int                timeout_milliseconds,
00093                                  DBusTimeoutHandler timeout_handler)
00094 {
00095   DBusPendingCall *pending;
00096   DBusTimeout *timeout;
00097 
00098   _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
00099  
00100   if (timeout_milliseconds == -1)
00101     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
00102 
00103   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
00104     return NULL;
00105   
00106   pending = dbus_new0 (DBusPendingCall, 1);
00107   
00108   if (pending == NULL)
00109     {
00110       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00111       return NULL;
00112     }
00113 
00114   if (timeout_milliseconds != _DBUS_INT_MAX)
00115     {
00116       timeout = _dbus_timeout_new (timeout_milliseconds,
00117                                    timeout_handler,
00118                                    pending, NULL);  
00119 
00120       if (timeout == NULL)
00121         {
00122           dbus_pending_call_free_data_slot (&notify_user_data_slot);
00123           dbus_free (pending);
00124           return NULL;
00125         }
00126 
00127       pending->timeout = timeout;
00128     }
00129   else
00130     {
00131       pending->timeout = NULL;
00132     }
00133       
00134   pending->refcount.value = 1;
00135   pending->connection = connection;
00136   _dbus_connection_ref_unlocked (pending->connection);
00137 
00138   _dbus_data_slot_list_init (&pending->slot_list);
00139   
00140   return pending;
00141 }
00142 
00151 void
00152 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
00153                                        DBusMessage     *message)
00154 {
00155   if (message == NULL)
00156     {
00157       message = pending->timeout_link->data;
00158       _dbus_list_clear (&pending->timeout_link);
00159     }
00160   else
00161     dbus_message_ref (message);
00162 
00163   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
00164                  message,
00165                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
00166                  "method return" :
00167                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
00168                  "error" : "other type",
00169                  pending->reply_serial);
00170   
00171   _dbus_assert (pending->reply == NULL);
00172   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
00173   pending->reply = message;
00174 }
00175 
00183 void
00184 _dbus_pending_call_complete (DBusPendingCall *pending)
00185 {
00186   _dbus_assert (!pending->completed);
00187   
00188   pending->completed = TRUE;
00189 
00190   if (pending->function)
00191     {
00192       void *user_data;
00193       user_data = dbus_pending_call_get_data (pending,
00194                                               notify_user_data_slot);
00195       
00196       (* pending->function) (pending, user_data);
00197     }
00198 }
00199 
00207 void
00208 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
00209                                                  DBusConnection  *connection)
00210 {
00211   _dbus_assert (connection == pending->connection);
00212   
00213   if (pending->timeout_link)
00214     {
00215       _dbus_connection_queue_synthesized_message_link (connection,
00216                                                        pending->timeout_link);
00217       pending->timeout_link = NULL;
00218     }
00219 }
00220 
00227 dbus_bool_t 
00228 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
00229 {
00230   _dbus_assert (pending != NULL);
00231 
00232   return pending->timeout_added;
00233 }
00234 
00235 
00242 void
00243 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
00244                                                dbus_bool_t       is_added)
00245 {
00246   _dbus_assert (pending != NULL);
00247 
00248   pending->timeout_added = is_added;
00249 }
00250 
00251 
00258 DBusTimeout *
00259 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
00260 {
00261   _dbus_assert (pending != NULL);
00262 
00263   return pending->timeout;
00264 }
00265 
00272 dbus_uint32_t 
00273 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
00274 {
00275   _dbus_assert (pending != NULL);
00276 
00277   return pending->reply_serial;
00278 }
00279 
00286 void
00287 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
00288                                                dbus_uint32_t serial)
00289 {
00290   _dbus_assert (pending != NULL);
00291   _dbus_assert (pending->reply_serial == 0);
00292 
00293   pending->reply_serial = serial;
00294 }
00295 
00302 DBusConnection *
00303 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
00304 {
00305   _dbus_assert (pending != NULL);
00306  
00307   CONNECTION_LOCK (pending->connection);
00308   return pending->connection;
00309 }
00310 
00317 DBusConnection *
00318 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
00319 {
00320   _dbus_assert (pending != NULL);
00321  
00322   return pending->connection;
00323 }
00324 
00333 dbus_bool_t
00334 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
00335                                                DBusMessage     *message,
00336                                                dbus_uint32_t    serial)
00337 { 
00338   DBusList *reply_link;
00339   DBusMessage *reply;
00340 
00341   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
00342                                   "Did not receive a reply. Possible causes include: "
00343                                   "the remote application did not send a reply, "
00344                                   "the message bus security policy blocked the reply, "
00345                                   "the reply timeout expired, or "
00346                                   "the network connection was broken.");
00347   if (reply == NULL)
00348     return FALSE;
00349 
00350   reply_link = _dbus_list_alloc_link (reply);
00351   if (reply_link == NULL)
00352     {
00353       dbus_message_unref (reply);
00354       return FALSE;
00355     }
00356 
00357   pending->timeout_link = reply_link;
00358 
00359   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
00360   
00361   return TRUE;
00362 }
00363 
00371 DBusPendingCall *
00372 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
00373 {
00374   pending->refcount.value += 1;
00375   
00376   return pending;
00377 }
00378 
00379 
00380 static void
00381 _dbus_pending_call_last_unref (DBusPendingCall *pending)
00382 {
00383   DBusConnection *connection;
00384   
00385   /* If we get here, we should be already detached
00386    * from the connection, or never attached.
00387    */
00388   _dbus_assert (!pending->timeout_added);  
00389 
00390   connection = pending->connection;
00391 
00392   /* this assumes we aren't holding connection lock... */
00393   _dbus_data_slot_list_free (&pending->slot_list);
00394 
00395   if (pending->timeout != NULL)
00396     _dbus_timeout_unref (pending->timeout);
00397       
00398   if (pending->timeout_link)
00399     {
00400       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
00401       _dbus_list_free_link (pending->timeout_link);
00402       pending->timeout_link = NULL;
00403     }
00404 
00405   if (pending->reply)
00406     {
00407       dbus_message_unref (pending->reply);
00408       pending->reply = NULL;
00409     }
00410       
00411   dbus_free (pending);
00412 
00413   dbus_pending_call_free_data_slot (&notify_user_data_slot);
00414 
00415   /* connection lock should not be held. */
00416   /* Free the connection last to avoid a weird state while
00417    * calling out to application code where the pending exists
00418    * but not the connection.
00419    */
00420   dbus_connection_unref (connection);
00421 }
00422 
00430 void
00431 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
00432 {
00433   dbus_bool_t last_unref;
00434   
00435   _dbus_assert (pending->refcount.value > 0);
00436 
00437   pending->refcount.value -= 1;
00438   last_unref = pending->refcount.value == 0;
00439 
00440   CONNECTION_UNLOCK (pending->connection);
00441   if (last_unref)
00442     _dbus_pending_call_last_unref (pending);
00443 }
00444 
00452 dbus_bool_t
00453 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
00454 {
00455   return pending->completed;
00456 }
00457 
00458 static DBusDataSlotAllocator slot_allocator;
00459 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
00460 
00474 dbus_bool_t
00475 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
00476                                      dbus_int32_t      slot,
00477                                      void             *data,
00478                                      DBusFreeFunction  free_data_func)
00479 {
00480   DBusFreeFunction old_free_func;
00481   void *old_data;
00482   dbus_bool_t retval;
00483 
00484   retval = _dbus_data_slot_list_set (&slot_allocator,
00485                                      &pending->slot_list,
00486                                      slot, data, free_data_func,
00487                                      &old_free_func, &old_data);
00488 
00489   /* Drop locks to call out to app code */
00490   CONNECTION_UNLOCK (pending->connection);
00491   
00492   if (retval)
00493     {
00494       if (old_free_func)
00495         (* old_free_func) (old_data);
00496     }
00497 
00498   CONNECTION_LOCK (pending->connection);
00499   
00500   return retval;
00501 }
00502 
00529 DBusPendingCall *
00530 dbus_pending_call_ref (DBusPendingCall *pending)
00531 {
00532   _dbus_return_val_if_fail (pending != NULL, NULL);
00533 
00534   /* The connection lock is better than the global
00535    * lock in the atomic increment fallback
00536    */
00537 #ifdef DBUS_HAVE_ATOMIC_INT
00538   _dbus_atomic_inc (&pending->refcount);
00539 #else
00540   CONNECTION_LOCK (pending->connection);
00541   _dbus_assert (pending->refcount.value > 0);
00542 
00543   pending->refcount.value += 1;
00544   CONNECTION_UNLOCK (pending->connection);
00545 #endif
00546   
00547   return pending;
00548 }
00549 
00556 void
00557 dbus_pending_call_unref (DBusPendingCall *pending)
00558 {
00559   dbus_bool_t last_unref;
00560 
00561   _dbus_return_if_fail (pending != NULL);
00562 
00563   /* More efficient to use the connection lock instead of atomic
00564    * int fallback if we lack atomic int decrement
00565    */
00566 #ifdef DBUS_HAVE_ATOMIC_INT
00567   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
00568 #else
00569   CONNECTION_LOCK (pending->connection);
00570   _dbus_assert (pending->refcount.value > 0);
00571   pending->refcount.value -= 1;
00572   last_unref = pending->refcount.value == 0;
00573   CONNECTION_UNLOCK (pending->connection);
00574 #endif
00575   
00576   if (last_unref)
00577     _dbus_pending_call_last_unref(pending);
00578 }
00579 
00590 dbus_bool_t
00591 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00592                               DBusPendingCallNotifyFunction function,
00593                               void                         *user_data,
00594                               DBusFreeFunction              free_user_data)
00595 {
00596   _dbus_return_val_if_fail (pending != NULL, FALSE);
00597 
00598   CONNECTION_LOCK (pending->connection);
00599   
00600   /* could invoke application code! */
00601   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00602                                              user_data, free_user_data))
00603     return FALSE;
00604   
00605   pending->function = function;
00606 
00607   CONNECTION_UNLOCK (pending->connection);
00608   
00609   return TRUE;
00610 }
00611 
00627 void
00628 dbus_pending_call_cancel (DBusPendingCall *pending)
00629 {
00630   _dbus_return_if_fail (pending != NULL);
00631 
00632   _dbus_connection_remove_pending_call (pending->connection,
00633                                         pending);
00634 }
00635 
00643 dbus_bool_t
00644 dbus_pending_call_get_completed (DBusPendingCall *pending)
00645 {
00646   dbus_bool_t completed;
00647   
00648   _dbus_return_val_if_fail (pending != NULL, FALSE);
00649 
00650   CONNECTION_LOCK (pending->connection);
00651   completed = pending->completed;
00652   CONNECTION_UNLOCK (pending->connection);
00653 
00654   return completed;
00655 }
00656 
00666 DBusMessage*
00667 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00668 {
00669   DBusMessage *message;
00670   
00671   _dbus_return_val_if_fail (pending != NULL, NULL);
00672   _dbus_return_val_if_fail (pending->completed, NULL);
00673   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00674 
00675   CONNECTION_LOCK (pending->connection);
00676   
00677   message = pending->reply;
00678   pending->reply = NULL;
00679 
00680   CONNECTION_UNLOCK (pending->connection);
00681   
00682   return message;
00683 }
00684 
00700 void
00701 dbus_pending_call_block (DBusPendingCall *pending)
00702 {
00703   _dbus_return_if_fail (pending != NULL);
00704 
00705   _dbus_connection_block_pending_call (pending);
00706 }
00707 
00722 dbus_bool_t
00723 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00724 {
00725   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00726 
00727   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00728                                           &_DBUS_LOCK_NAME (pending_call_slots),
00729                                           slot_p);
00730 }
00731 
00743 void
00744 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00745 {
00746   _dbus_return_if_fail (slot_p != NULL);
00747   _dbus_return_if_fail (*slot_p >= 0);
00748 
00749   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00750 }
00751 
00765 dbus_bool_t
00766 dbus_pending_call_set_data (DBusPendingCall  *pending,
00767                             dbus_int32_t      slot,
00768                             void             *data,
00769                             DBusFreeFunction  free_data_func)
00770 {
00771   dbus_bool_t retval;
00772   
00773   _dbus_return_val_if_fail (pending != NULL, FALSE);
00774   _dbus_return_val_if_fail (slot >= 0, FALSE);
00775 
00776   
00777   CONNECTION_LOCK (pending->connection);
00778   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00779   CONNECTION_UNLOCK (pending->connection);
00780   return retval;
00781 }
00782 
00791 void*
00792 dbus_pending_call_get_data (DBusPendingCall   *pending,
00793                             dbus_int32_t       slot)
00794 {
00795   void *res;
00796 
00797   _dbus_return_val_if_fail (pending != NULL, NULL);
00798 
00799   CONNECTION_LOCK (pending->connection);
00800   res = _dbus_data_slot_list_get (&slot_allocator,
00801                                   &pending->slot_list,
00802                                   slot);
00803   CONNECTION_UNLOCK (pending->connection);
00804 
00805   return res;
00806 }
00807 
00810 #ifdef DBUS_BUILD_TESTS
00811 
00818 dbus_bool_t
00819 _dbus_pending_call_test (const char *test_data_dir)
00820 {  
00821 
00822   return TRUE;
00823 }
00824 #endif /* DBUS_BUILD_TESTS */

Generated on Thu Oct 4 2012 08:02:56 for D-Bus by  doxygen 1.7.1