dbus-pending-call.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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   /* it would probably seem logical to pass in _DBUS_INT_MAX for
00104    * infinite timeout, but then math in
00105    * _dbus_connection_block_for_reply would get all overflow-prone, so
00106    * smack that down.
00107    */
00108   if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6)
00109     timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6;
00110   
00111   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
00112     return NULL;
00113   
00114   pending = dbus_new0 (DBusPendingCall, 1);
00115   
00116   if (pending == NULL)
00117     {
00118       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00119       return NULL;
00120     }
00121 
00122   timeout = _dbus_timeout_new (timeout_milliseconds,
00123                                timeout_handler,
00124                                pending, NULL);  
00125 
00126   if (timeout == NULL)
00127     {
00128       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00129       dbus_free (pending);
00130       return NULL;
00131     }
00132   
00133   pending->refcount.value = 1;
00134   pending->connection = connection;
00135   _dbus_connection_ref_unlocked (pending->connection);
00136 
00137   pending->timeout = timeout;
00138 
00139 
00140   _dbus_data_slot_list_init (&pending->slot_list);
00141   
00142   return pending;
00143 }
00144 
00153 void
00154 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
00155                                        DBusMessage     *message)
00156 {
00157   if (message == NULL)
00158     {
00159       message = pending->timeout_link->data;
00160       _dbus_list_clear (&pending->timeout_link);
00161     }
00162   else
00163     dbus_message_ref (message);
00164 
00165   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
00166                  message,
00167                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
00168                  "method return" :
00169                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
00170                  "error" : "other type",
00171                  pending->reply_serial);
00172   
00173   _dbus_assert (pending->reply == NULL);
00174   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
00175   pending->reply = message;
00176 }
00177 
00185 void
00186 _dbus_pending_call_complete (DBusPendingCall *pending)
00187 {
00188   _dbus_assert (!pending->completed);
00189   
00190   pending->completed = TRUE;
00191 
00192   if (pending->function)
00193     {
00194       void *user_data;
00195       user_data = dbus_pending_call_get_data (pending,
00196                                               notify_user_data_slot);
00197       
00198       (* pending->function) (pending, user_data);
00199     }
00200 }
00201 
00209 void
00210 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
00211                                                  DBusConnection  *connection)
00212 {
00213   _dbus_assert (connection == pending->connection);
00214   
00215   if (pending->timeout_link)
00216     {
00217       _dbus_connection_queue_synthesized_message_link (connection,
00218                                                        pending->timeout_link);
00219       pending->timeout_link = NULL;
00220     }
00221 }
00222 
00229 dbus_bool_t 
00230 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
00231 {
00232   _dbus_assert (pending != NULL);
00233 
00234   return pending->timeout_added;
00235 }
00236 
00237 
00244 void
00245 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
00246                                                dbus_bool_t       is_added)
00247 {
00248   _dbus_assert (pending != NULL);
00249 
00250   pending->timeout_added = is_added;
00251 }
00252 
00253 
00260 DBusTimeout *
00261 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
00262 {
00263   _dbus_assert (pending != NULL);
00264 
00265   return pending->timeout;
00266 }
00267 
00274 dbus_uint32_t 
00275 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
00276 {
00277   _dbus_assert (pending != NULL);
00278 
00279   return pending->reply_serial;
00280 }
00281 
00288 void
00289 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
00290                                                dbus_uint32_t serial)
00291 {
00292   _dbus_assert (pending != NULL);
00293   _dbus_assert (pending->reply_serial == 0);
00294 
00295   pending->reply_serial = serial;
00296 }
00297 
00304 DBusConnection *
00305 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
00306 {
00307   _dbus_assert (pending != NULL);
00308  
00309   CONNECTION_LOCK (pending->connection);
00310   return pending->connection;
00311 }
00312 
00319 DBusConnection *
00320 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
00321 {
00322   _dbus_assert (pending != NULL);
00323  
00324   return pending->connection;
00325 }
00326 
00335 dbus_bool_t
00336 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
00337                                                DBusMessage     *message,
00338                                                dbus_uint32_t    serial)
00339 { 
00340   DBusList *reply_link;
00341   DBusMessage *reply;
00342 
00343   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
00344                                   "Did not receive a reply. Possible causes include: "
00345                                   "the remote application did not send a reply, "
00346                                   "the message bus security policy blocked the reply, "
00347                                   "the reply timeout expired, or "
00348                                   "the network connection was broken.");
00349   if (reply == NULL)
00350     return FALSE;
00351 
00352   reply_link = _dbus_list_alloc_link (reply);
00353   if (reply_link == NULL)
00354     {
00355       dbus_message_unref (reply);
00356       return FALSE;
00357     }
00358 
00359   pending->timeout_link = reply_link;
00360 
00361   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
00362   
00363   return TRUE;
00364 }
00365 
00373 DBusPendingCall *
00374 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
00375 {
00376   pending->refcount.value += 1;
00377   
00378   return pending;
00379 }
00380 
00381 
00382 static void
00383 _dbus_pending_call_last_unref (DBusPendingCall *pending)
00384 {
00385   DBusConnection *connection;
00386   
00387   /* If we get here, we should be already detached
00388    * from the connection, or never attached.
00389    */
00390   _dbus_assert (!pending->timeout_added);  
00391 
00392   connection = pending->connection;
00393 
00394   /* this assumes we aren't holding connection lock... */
00395   _dbus_data_slot_list_free (&pending->slot_list);
00396 
00397   if (pending->timeout != NULL)
00398     _dbus_timeout_unref (pending->timeout);
00399       
00400   if (pending->timeout_link)
00401     {
00402       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
00403       _dbus_list_free_link (pending->timeout_link);
00404       pending->timeout_link = NULL;
00405     }
00406 
00407   if (pending->reply)
00408     {
00409       dbus_message_unref (pending->reply);
00410       pending->reply = NULL;
00411     }
00412       
00413   dbus_free (pending);
00414 
00415   dbus_pending_call_free_data_slot (&notify_user_data_slot);
00416 
00417   /* connection lock should not be held. */
00418   /* Free the connection last to avoid a weird state while
00419    * calling out to application code where the pending exists
00420    * but not the connection.
00421    */
00422   dbus_connection_unref (connection);
00423 }
00424 
00432 void
00433 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
00434 {
00435   dbus_bool_t last_unref;
00436   
00437   _dbus_assert (pending->refcount.value > 0);
00438 
00439   pending->refcount.value -= 1;
00440   last_unref = pending->refcount.value == 0;
00441 
00442   CONNECTION_UNLOCK (pending->connection);
00443   if (last_unref)
00444     _dbus_pending_call_last_unref (pending);
00445 }
00446 
00454 dbus_bool_t
00455 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
00456 {
00457   return pending->completed;
00458 }
00459 
00460 static DBusDataSlotAllocator slot_allocator;
00461 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
00462 
00476 dbus_bool_t
00477 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
00478                                      dbus_int32_t      slot,
00479                                      void             *data,
00480                                      DBusFreeFunction  free_data_func)
00481 {
00482   DBusFreeFunction old_free_func;
00483   void *old_data;
00484   dbus_bool_t retval;
00485 
00486   retval = _dbus_data_slot_list_set (&slot_allocator,
00487                                      &pending->slot_list,
00488                                      slot, data, free_data_func,
00489                                      &old_free_func, &old_data);
00490 
00491   /* Drop locks to call out to app code */
00492   CONNECTION_UNLOCK (pending->connection);
00493   
00494   if (retval)
00495     {
00496       if (old_free_func)
00497         (* old_free_func) (old_data);
00498     }
00499 
00500   CONNECTION_LOCK (pending->connection);
00501   
00502   return retval;
00503 }
00504 
00531 DBusPendingCall *
00532 dbus_pending_call_ref (DBusPendingCall *pending)
00533 {
00534   _dbus_return_val_if_fail (pending != NULL, NULL);
00535 
00536   /* The connection lock is better than the global
00537    * lock in the atomic increment fallback
00538    */
00539 #ifdef DBUS_HAVE_ATOMIC_INT
00540   _dbus_atomic_inc (&pending->refcount);
00541 #else
00542   CONNECTION_LOCK (pending->connection);
00543   _dbus_assert (pending->refcount.value > 0);
00544 
00545   pending->refcount.value += 1;
00546   CONNECTION_UNLOCK (pending->connection);
00547 #endif
00548   
00549   return pending;
00550 }
00551 
00558 void
00559 dbus_pending_call_unref (DBusPendingCall *pending)
00560 {
00561   dbus_bool_t last_unref;
00562 
00563   _dbus_return_if_fail (pending != NULL);
00564 
00565   /* More efficient to use the connection lock instead of atomic
00566    * int fallback if we lack atomic int decrement
00567    */
00568 #ifdef DBUS_HAVE_ATOMIC_INT
00569   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
00570 #else
00571   CONNECTION_LOCK (pending->connection);
00572   _dbus_assert (pending->refcount.value > 0);
00573   pending->refcount.value -= 1;
00574   last_unref = pending->refcount.value == 0;
00575   CONNECTION_UNLOCK (pending->connection);
00576 #endif
00577   
00578   if (last_unref)
00579     _dbus_pending_call_last_unref(pending);
00580 }
00581 
00592 dbus_bool_t
00593 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00594                               DBusPendingCallNotifyFunction function,
00595                               void                         *user_data,
00596                               DBusFreeFunction              free_user_data)
00597 {
00598   _dbus_return_val_if_fail (pending != NULL, FALSE);
00599 
00600   CONNECTION_LOCK (pending->connection);
00601   
00602   /* could invoke application code! */
00603   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00604                                              user_data, free_user_data))
00605     return FALSE;
00606   
00607   pending->function = function;
00608 
00609   CONNECTION_UNLOCK (pending->connection);
00610   
00611   return TRUE;
00612 }
00613 
00629 void
00630 dbus_pending_call_cancel (DBusPendingCall *pending)
00631 {
00632   _dbus_return_if_fail (pending != NULL);
00633 
00634   _dbus_connection_remove_pending_call (pending->connection,
00635                                         pending);
00636 }
00637 
00645 dbus_bool_t
00646 dbus_pending_call_get_completed (DBusPendingCall *pending)
00647 {
00648   dbus_bool_t completed;
00649   
00650   _dbus_return_val_if_fail (pending != NULL, FALSE);
00651 
00652   CONNECTION_LOCK (pending->connection);
00653   completed = pending->completed;
00654   CONNECTION_UNLOCK (pending->connection);
00655 
00656   return completed;
00657 }
00658 
00668 DBusMessage*
00669 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00670 {
00671   DBusMessage *message;
00672   
00673   _dbus_return_val_if_fail (pending != NULL, NULL);
00674   _dbus_return_val_if_fail (pending->completed, NULL);
00675   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00676 
00677   CONNECTION_LOCK (pending->connection);
00678   
00679   message = pending->reply;
00680   pending->reply = NULL;
00681 
00682   CONNECTION_UNLOCK (pending->connection);
00683   
00684   return message;
00685 }
00686 
00702 void
00703 dbus_pending_call_block (DBusPendingCall *pending)
00704 {
00705   _dbus_return_if_fail (pending != NULL);
00706 
00707   _dbus_connection_block_pending_call (pending);
00708 }
00709 
00724 dbus_bool_t
00725 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00726 {
00727   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00728 
00729   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00730                                           &_DBUS_LOCK_NAME (pending_call_slots),
00731                                           slot_p);
00732 }
00733 
00745 void
00746 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00747 {
00748   _dbus_return_if_fail (slot_p != NULL);
00749   _dbus_return_if_fail (*slot_p >= 0);
00750 
00751   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00752 }
00753 
00767 dbus_bool_t
00768 dbus_pending_call_set_data (DBusPendingCall  *pending,
00769                             dbus_int32_t      slot,
00770                             void             *data,
00771                             DBusFreeFunction  free_data_func)
00772 {
00773   dbus_bool_t retval;
00774   
00775   _dbus_return_val_if_fail (pending != NULL, FALSE);
00776   _dbus_return_val_if_fail (slot >= 0, FALSE);
00777 
00778   
00779   CONNECTION_LOCK (pending->connection);
00780   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00781   CONNECTION_UNLOCK (pending->connection);
00782   return retval;
00783 }
00784 
00793 void*
00794 dbus_pending_call_get_data (DBusPendingCall   *pending,
00795                             dbus_int32_t       slot)
00796 {
00797   void *res;
00798 
00799   _dbus_return_val_if_fail (pending != NULL, NULL);
00800 
00801   CONNECTION_LOCK (pending->connection);
00802   res = _dbus_data_slot_list_get (&slot_allocator,
00803                                   &pending->slot_list,
00804                                   slot);
00805   CONNECTION_UNLOCK (pending->connection);
00806 
00807   return res;
00808 }
00809 
00812 #ifdef DBUS_BUILD_TESTS
00813 
00820 dbus_bool_t
00821 _dbus_pending_call_test (const char *test_data_dir)
00822 {  
00823 
00824   return TRUE;
00825 }
00826 #endif /* DBUS_BUILD_TESTS */

Generated on Tue Apr 15 15:54:00 2008 for D-Bus by  doxygen 1.5.1