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 
00047 #define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
00048 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
00049 
00050 struct DBusPendingCall
00051 {
00052   DBusAtomic refcount;                            
00054   DBusDataSlotList slot_list;                     
00056   DBusPendingCallNotifyFunction function;         
00058   DBusConnection *connection;                     
00059   DBusMessage *reply;                             
00060   DBusTimeout *timeout;                           
00062   DBusList *timeout_link;                         
00064   dbus_uint32_t reply_serial;                     
00066   unsigned int completed : 1;                     
00067   unsigned int timeout_added : 1;                 
00068 };
00069 
00070 static dbus_int32_t notify_user_data_slot = -1;
00071 
00080 DBusPendingCall*
00081 _dbus_pending_call_new_unlocked (DBusConnection    *connection,
00082                                  int                timeout_milliseconds,
00083                                  DBusTimeoutHandler timeout_handler)
00084 {
00085   DBusPendingCall *pending;
00086   DBusTimeout *timeout;
00087 
00088   _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
00089  
00090   if (timeout_milliseconds == -1)
00091     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
00092 
00093   /* it would probably seem logical to pass in _DBUS_INT_MAX for
00094    * infinite timeout, but then math in
00095    * _dbus_connection_block_for_reply would get all overflow-prone, so
00096    * smack that down.
00097    */
00098   if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6)
00099     timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6;
00100   
00101   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
00102     return NULL;
00103   
00104   pending = dbus_new0 (DBusPendingCall, 1);
00105   
00106   if (pending == NULL)
00107     {
00108       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00109       return NULL;
00110     }
00111 
00112   timeout = _dbus_timeout_new (timeout_milliseconds,
00113                                timeout_handler,
00114                                pending, NULL);  
00115 
00116   if (timeout == NULL)
00117     {
00118       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00119       dbus_free (pending);
00120       return NULL;
00121     }
00122   
00123   pending->refcount.value = 1;
00124   pending->connection = connection;
00125   _dbus_connection_ref_unlocked (pending->connection);
00126 
00127   pending->timeout = timeout;
00128 
00129 
00130   _dbus_data_slot_list_init (&pending->slot_list);
00131   
00132   return pending;
00133 }
00134 
00143 void
00144 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
00145                                        DBusMessage     *message)
00146 {
00147   if (message == NULL)
00148     {
00149       message = pending->timeout_link->data;
00150       _dbus_list_clear (&pending->timeout_link);
00151     }
00152   else
00153     dbus_message_ref (message);
00154 
00155   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
00156                  message,
00157                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
00158                  "method return" :
00159                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
00160                  "error" : "other type",
00161                  pending->reply_serial);
00162   
00163   _dbus_assert (pending->reply == NULL);
00164   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
00165   pending->reply = message;
00166 }
00167 
00175 void
00176 _dbus_pending_call_complete (DBusPendingCall *pending)
00177 {
00178   _dbus_assert (!pending->completed);
00179   
00180   pending->completed = TRUE;
00181 
00182   if (pending->function)
00183     {
00184       void *user_data;
00185       user_data = dbus_pending_call_get_data (pending,
00186                                               notify_user_data_slot);
00187       
00188       (* pending->function) (pending, user_data);
00189     }
00190 }
00191 
00192 void
00193 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
00194                                                  DBusConnection  *connection)
00195 {
00196   _dbus_assert (connection == pending->connection);
00197   
00198   if (pending->timeout_link)
00199     {
00200       _dbus_connection_queue_synthesized_message_link (connection,
00201                                                        pending->timeout_link);
00202       pending->timeout_link = NULL;
00203     }
00204 }
00205 
00212 dbus_bool_t 
00213 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
00214 {
00215   _dbus_assert (pending != NULL);
00216 
00217   return pending->timeout_added;
00218 }
00219 
00220 
00227 void
00228 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
00229                                                dbus_bool_t       is_added)
00230 {
00231   _dbus_assert (pending != NULL);
00232 
00233   pending->timeout_added = is_added;
00234 }
00235 
00236 
00243 DBusTimeout *
00244 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
00245 {
00246   _dbus_assert (pending != NULL);
00247 
00248   return pending->timeout;
00249 }
00250 
00257 dbus_uint32_t 
00258 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
00259 {
00260   _dbus_assert (pending != NULL);
00261 
00262   return pending->reply_serial;
00263 }
00264 
00271 void
00272 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
00273                                                dbus_uint32_t serial)
00274 {
00275   _dbus_assert (pending != NULL);
00276   _dbus_assert (pending->reply_serial == 0);
00277 
00278   pending->reply_serial = serial;
00279 }
00280 
00287 DBusConnection *
00288 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
00289 {
00290   _dbus_assert (pending != NULL);
00291  
00292   CONNECTION_LOCK (pending->connection);
00293   return pending->connection;
00294 }
00295 
00302 DBusConnection *
00303 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
00304 {
00305   _dbus_assert (pending != NULL);
00306  
00307   return pending->connection;
00308 }
00309 
00318 dbus_bool_t
00319 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
00320                                                DBusMessage     *message,
00321                                                dbus_uint32_t    serial)
00322 { 
00323   DBusList *reply_link;
00324   DBusMessage *reply;
00325 
00326   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
00327                                   "Did not receive a reply. Possible causes include: "
00328                                   "the remote application did not send a reply, "
00329                                   "the message bus security policy blocked the reply, "
00330                                   "the reply timeout expired, or "
00331                                   "the network connection was broken.");
00332   if (reply == NULL)
00333     return FALSE;
00334 
00335   reply_link = _dbus_list_alloc_link (reply);
00336   if (reply_link == NULL)
00337     {
00338       dbus_message_unref (reply);
00339       return FALSE;
00340     }
00341 
00342   pending->timeout_link = reply_link;
00343 
00344   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
00345   
00346   return TRUE;
00347 }
00348 
00376 DBusPendingCall *
00377 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
00378 {
00379   pending->refcount.value += 1;
00380   
00381   return pending;
00382 }
00383 
00390 DBusPendingCall *
00391 dbus_pending_call_ref (DBusPendingCall *pending)
00392 {
00393   _dbus_return_val_if_fail (pending != NULL, NULL);
00394 
00395   /* The connection lock is better than the global
00396    * lock in the atomic increment fallback
00397    */
00398 #ifdef DBUS_HAVE_ATOMIC_INT
00399   _dbus_atomic_inc (&pending->refcount);
00400 #else
00401   CONNECTION_LOCK (pending->connection);
00402   _dbus_assert (pending->refcount.value > 0);
00403 
00404   pending->refcount.value += 1;
00405   CONNECTION_UNLOCK (pending->connection);
00406 #endif
00407   
00408   return pending;
00409 }
00410 
00411 static void
00412 _dbus_pending_call_last_unref (DBusPendingCall *pending)
00413 {
00414   DBusConnection *connection;
00415   
00416   /* If we get here, we should be already detached
00417    * from the connection, or never attached.
00418    */
00419   _dbus_assert (!pending->timeout_added);  
00420 
00421   connection = pending->connection;
00422 
00423   /* this assumes we aren't holding connection lock... */
00424   _dbus_data_slot_list_free (&pending->slot_list);
00425 
00426   if (pending->timeout != NULL)
00427     _dbus_timeout_unref (pending->timeout);
00428       
00429   if (pending->timeout_link)
00430     {
00431       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
00432       _dbus_list_free_link (pending->timeout_link);
00433       pending->timeout_link = NULL;
00434     }
00435 
00436   if (pending->reply)
00437     {
00438       dbus_message_unref (pending->reply);
00439       pending->reply = NULL;
00440     }
00441       
00442   dbus_free (pending);
00443 
00444   dbus_pending_call_free_data_slot (&notify_user_data_slot);
00445 
00446   /* connection lock should not be held. */
00447   /* Free the connection last to avoid a weird state while
00448    * calling out to application code where the pending exists
00449    * but not the connection.
00450    */
00451   dbus_connection_unref (connection);
00452 }
00453 
00461 void
00462 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
00463 {
00464   dbus_bool_t last_unref;
00465   
00466   _dbus_assert (pending->refcount.value > 0);
00467 
00468   pending->refcount.value -= 1;
00469   last_unref = pending->refcount.value == 0;
00470 
00471   CONNECTION_UNLOCK (pending->connection);
00472   if (last_unref)
00473     _dbus_pending_call_last_unref (pending);
00474 }
00475 
00482 void
00483 dbus_pending_call_unref (DBusPendingCall *pending)
00484 {
00485   dbus_bool_t last_unref;
00486 
00487   _dbus_return_if_fail (pending != NULL);
00488 
00489   /* More efficient to use the connection lock instead of atomic
00490    * int fallback if we lack atomic int decrement
00491    */
00492 #ifdef DBUS_HAVE_ATOMIC_INT
00493   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
00494 #else
00495   CONNECTION_LOCK (pending->connection);
00496   _dbus_assert (pending->refcount.value > 0);
00497   pending->refcount.value -= 1;
00498   last_unref = pending->refcount.value == 0;
00499   CONNECTION_UNLOCK (pending->connection);
00500 #endif
00501   
00502   if (last_unref)
00503     _dbus_pending_call_last_unref(pending);
00504 }
00505 
00516 dbus_bool_t
00517 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00518                               DBusPendingCallNotifyFunction function,
00519                               void                         *user_data,
00520                               DBusFreeFunction              free_user_data)
00521 {
00522   _dbus_return_val_if_fail (pending != NULL, FALSE);
00523 
00524   CONNECTION_LOCK (pending->connection);
00525   
00526   /* could invoke application code! */
00527   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00528                                              user_data, free_user_data))
00529     return FALSE;
00530   
00531   pending->function = function;
00532 
00533   CONNECTION_UNLOCK (pending->connection);
00534   
00535   return TRUE;
00536 }
00537 
00547 void
00548 dbus_pending_call_cancel (DBusPendingCall *pending)
00549 {
00550   _dbus_return_if_fail (pending != NULL);
00551 
00552   _dbus_connection_remove_pending_call (pending->connection,
00553                                         pending);
00554 }
00555 
00563 dbus_bool_t
00564 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
00565 {
00566   return pending->completed;
00567 }
00568 
00576 dbus_bool_t
00577 dbus_pending_call_get_completed (DBusPendingCall *pending)
00578 {
00579   dbus_bool_t completed;
00580   
00581   _dbus_return_val_if_fail (pending != NULL, FALSE);
00582 
00583   CONNECTION_LOCK (pending->connection);
00584   completed = pending->completed;
00585   CONNECTION_UNLOCK (pending->connection);
00586 
00587   return completed;
00588 }
00589 
00599 DBusMessage*
00600 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00601 {
00602   DBusMessage *message;
00603   
00604   _dbus_return_val_if_fail (pending != NULL, NULL);
00605   _dbus_return_val_if_fail (pending->completed, NULL);
00606   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00607 
00608   CONNECTION_LOCK (pending->connection);
00609   
00610   message = pending->reply;
00611   pending->reply = NULL;
00612 
00613   CONNECTION_UNLOCK (pending->connection);
00614   
00615   return message;
00616 }
00617 
00633 void
00634 dbus_pending_call_block (DBusPendingCall *pending)
00635 {
00636   _dbus_return_if_fail (pending != NULL);
00637 
00638   _dbus_connection_block_pending_call (pending);
00639 }
00640 
00641 static DBusDataSlotAllocator slot_allocator;
00642 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
00643 
00658 dbus_bool_t
00659 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00660 {
00661   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00662 
00663   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00664                                           &_DBUS_LOCK_NAME (pending_call_slots),
00665                                           slot_p);
00666 }
00667 
00679 void
00680 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00681 {
00682   _dbus_return_if_fail (slot_p != NULL);
00683   _dbus_return_if_fail (*slot_p >= 0);
00684 
00685   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00686 }
00687 
00701 dbus_bool_t
00702 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
00703                                      dbus_int32_t      slot,
00704                                      void             *data,
00705                                      DBusFreeFunction  free_data_func)
00706 {
00707   DBusFreeFunction old_free_func;
00708   void *old_data;
00709   dbus_bool_t retval;
00710 
00711   retval = _dbus_data_slot_list_set (&slot_allocator,
00712                                      &pending->slot_list,
00713                                      slot, data, free_data_func,
00714                                      &old_free_func, &old_data);
00715 
00716   /* Drop locks to call out to app code */
00717   CONNECTION_UNLOCK (pending->connection);
00718   
00719   if (retval)
00720     {
00721       if (old_free_func)
00722         (* old_free_func) (old_data);
00723     }
00724 
00725   CONNECTION_LOCK (pending->connection);
00726   
00727   return retval;
00728 }
00729 
00743 dbus_bool_t
00744 dbus_pending_call_set_data (DBusPendingCall  *pending,
00745                             dbus_int32_t      slot,
00746                             void             *data,
00747                             DBusFreeFunction  free_data_func)
00748 {
00749   dbus_bool_t retval;
00750   
00751   _dbus_return_val_if_fail (pending != NULL, FALSE);
00752   _dbus_return_val_if_fail (slot >= 0, FALSE);
00753 
00754   
00755   CONNECTION_LOCK (pending->connection);
00756   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00757   CONNECTION_UNLOCK (pending->connection);
00758   return retval;
00759 }
00760 
00769 void*
00770 dbus_pending_call_get_data (DBusPendingCall   *pending,
00771                             dbus_int32_t       slot)
00772 {
00773   void *res;
00774 
00775   _dbus_return_val_if_fail (pending != NULL, NULL);
00776 
00777   CONNECTION_LOCK (pending->connection);
00778   res = _dbus_data_slot_list_get (&slot_allocator,
00779                                   &pending->slot_list,
00780                                   slot);
00781   CONNECTION_UNLOCK (pending->connection);
00782 
00783   return res;
00784 }
00785 
00788 #ifdef DBUS_BUILD_TESTS
00789 
00796 dbus_bool_t
00797 _dbus_pending_call_test (const char *test_data_dir)
00798 {  
00799 
00800   return TRUE;
00801 }
00802 #endif /* DBUS_BUILD_TESTS */

Generated on Fri Oct 20 11:46:24 2006 for D-Bus by  doxygen 1.4.7