D-Bus 1.4.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-pending-call-internal.h"
00028 #include "dbus-pending-call.h"
00029 #include "dbus-list.h"
00030 #include "dbus-threads.h"
00031 #include "dbus-test.h"
00032 
00052 #define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
00053 
00056 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
00057 
00061 struct DBusPendingCall
00062 {
00063   DBusAtomic refcount;                            
00065   DBusDataSlotList slot_list;                     
00067   DBusPendingCallNotifyFunction function;         
00069   DBusConnection *connection;                     
00070   DBusMessage *reply;                             
00071   DBusTimeout *timeout;                           
00073   DBusList *timeout_link;                         
00075   dbus_uint32_t reply_serial;                     
00077   unsigned int completed : 1;                     
00078   unsigned int timeout_added : 1;                 
00079 };
00080 
00081 static dbus_int32_t notify_user_data_slot = -1;
00082 
00093 DBusPendingCall*
00094 _dbus_pending_call_new_unlocked (DBusConnection    *connection,
00095                                  int                timeout_milliseconds,
00096                                  DBusTimeoutHandler timeout_handler)
00097 {
00098   DBusPendingCall *pending;
00099   DBusTimeout *timeout;
00100 
00101   _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
00102  
00103   if (timeout_milliseconds == -1)
00104     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
00105 
00106   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
00107     return NULL;
00108   
00109   pending = dbus_new0 (DBusPendingCall, 1);
00110   
00111   if (pending == NULL)
00112     {
00113       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00114       return NULL;
00115     }
00116 
00117   if (timeout_milliseconds != DBUS_TIMEOUT_INFINITE)
00118     {
00119       timeout = _dbus_timeout_new (timeout_milliseconds,
00120                                    timeout_handler,
00121                                    pending, NULL);  
00122 
00123       if (timeout == NULL)
00124         {
00125           dbus_pending_call_free_data_slot (&notify_user_data_slot);
00126           dbus_free (pending);
00127           return NULL;
00128         }
00129 
00130       pending->timeout = timeout;
00131     }
00132   else
00133     {
00134       pending->timeout = NULL;
00135     }
00136       
00137   pending->refcount.value = 1;
00138   pending->connection = connection;
00139   _dbus_connection_ref_unlocked (pending->connection);
00140 
00141   _dbus_data_slot_list_init (&pending->slot_list);
00142   
00143   return pending;
00144 }
00145 
00154 void
00155 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
00156                                        DBusMessage     *message)
00157 {
00158   if (message == NULL)
00159     {
00160       message = pending->timeout_link->data;
00161       _dbus_list_clear (&pending->timeout_link);
00162     }
00163   else
00164     dbus_message_ref (message);
00165 
00166   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
00167                  message,
00168                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
00169                  "method return" :
00170                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
00171                  "error" : "other type",
00172                  pending->reply_serial);
00173   
00174   _dbus_assert (pending->reply == NULL);
00175   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
00176   pending->reply = message;
00177 }
00178 
00186 void
00187 _dbus_pending_call_complete (DBusPendingCall *pending)
00188 {
00189   _dbus_assert (!pending->completed);
00190   
00191   pending->completed = TRUE;
00192 
00193   if (pending->function)
00194     {
00195       void *user_data;
00196       user_data = dbus_pending_call_get_data (pending,
00197                                               notify_user_data_slot);
00198       
00199       (* pending->function) (pending, user_data);
00200     }
00201 }
00202 
00210 void
00211 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
00212                                                  DBusConnection  *connection)
00213 {
00214   _dbus_assert (connection == pending->connection);
00215   
00216   if (pending->timeout_link)
00217     {
00218       _dbus_connection_queue_synthesized_message_link (connection,
00219                                                        pending->timeout_link);
00220       pending->timeout_link = NULL;
00221     }
00222 }
00223 
00230 dbus_bool_t 
00231 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
00232 {
00233   _dbus_assert (pending != NULL);
00234 
00235   return pending->timeout_added;
00236 }
00237 
00238 
00245 void
00246 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
00247                                                dbus_bool_t       is_added)
00248 {
00249   _dbus_assert (pending != NULL);
00250 
00251   pending->timeout_added = is_added;
00252 }
00253 
00254 
00261 DBusTimeout *
00262 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
00263 {
00264   _dbus_assert (pending != NULL);
00265 
00266   return pending->timeout;
00267 }
00268 
00275 dbus_uint32_t 
00276 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
00277 {
00278   _dbus_assert (pending != NULL);
00279 
00280   return pending->reply_serial;
00281 }
00282 
00289 void
00290 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
00291                                                dbus_uint32_t serial)
00292 {
00293   _dbus_assert (pending != NULL);
00294   _dbus_assert (pending->reply_serial == 0);
00295 
00296   pending->reply_serial = serial;
00297 }
00298 
00305 DBusConnection *
00306 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
00307 {
00308   _dbus_assert (pending != NULL);
00309  
00310   CONNECTION_LOCK (pending->connection);
00311   return pending->connection;
00312 }
00313 
00320 DBusConnection *
00321 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
00322 {
00323   _dbus_assert (pending != NULL);
00324  
00325   return pending->connection;
00326 }
00327 
00336 dbus_bool_t
00337 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
00338                                                DBusMessage     *message,
00339                                                dbus_uint32_t    serial)
00340 { 
00341   DBusList *reply_link;
00342   DBusMessage *reply;
00343 
00344   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
00345                                   "Did not receive a reply. Possible causes include: "
00346                                   "the remote application did not send a reply, "
00347                                   "the message bus security policy blocked the reply, "
00348                                   "the reply timeout expired, or "
00349                                   "the network connection was broken.");
00350   if (reply == NULL)
00351     return FALSE;
00352 
00353   reply_link = _dbus_list_alloc_link (reply);
00354   if (reply_link == NULL)
00355     {
00356       dbus_message_unref (reply);
00357       return FALSE;
00358     }
00359 
00360   pending->timeout_link = reply_link;
00361 
00362   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
00363   
00364   return TRUE;
00365 }
00366 
00374 DBusPendingCall *
00375 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
00376 {
00377   pending->refcount.value += 1;
00378   
00379   return pending;
00380 }
00381 
00382 
00383 static void
00384 _dbus_pending_call_last_unref (DBusPendingCall *pending)
00385 {
00386   DBusConnection *connection;
00387   
00388   /* If we get here, we should be already detached
00389    * from the connection, or never attached.
00390    */
00391   _dbus_assert (!pending->timeout_added);  
00392 
00393   connection = pending->connection;
00394 
00395   /* this assumes we aren't holding connection lock... */
00396   _dbus_data_slot_list_free (&pending->slot_list);
00397 
00398   if (pending->timeout != NULL)
00399     _dbus_timeout_unref (pending->timeout);
00400       
00401   if (pending->timeout_link)
00402     {
00403       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
00404       _dbus_list_free_link (pending->timeout_link);
00405       pending->timeout_link = NULL;
00406     }
00407 
00408   if (pending->reply)
00409     {
00410       dbus_message_unref (pending->reply);
00411       pending->reply = NULL;
00412     }
00413       
00414   dbus_free (pending);
00415 
00416   dbus_pending_call_free_data_slot (&notify_user_data_slot);
00417 
00418   /* connection lock should not be held. */
00419   /* Free the connection last to avoid a weird state while
00420    * calling out to application code where the pending exists
00421    * but not the connection.
00422    */
00423   dbus_connection_unref (connection);
00424 }
00425 
00433 void
00434 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
00435 {
00436   dbus_bool_t last_unref;
00437   
00438   _dbus_assert (pending->refcount.value > 0);
00439 
00440   pending->refcount.value -= 1;
00441   last_unref = pending->refcount.value == 0;
00442 
00443   CONNECTION_UNLOCK (pending->connection);
00444   if (last_unref)
00445     _dbus_pending_call_last_unref (pending);
00446 }
00447 
00455 dbus_bool_t
00456 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
00457 {
00458   return pending->completed;
00459 }
00460 
00461 static DBusDataSlotAllocator slot_allocator;
00462 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
00463 
00477 dbus_bool_t
00478 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
00479                                      dbus_int32_t      slot,
00480                                      void             *data,
00481                                      DBusFreeFunction  free_data_func)
00482 {
00483   DBusFreeFunction old_free_func;
00484   void *old_data;
00485   dbus_bool_t retval;
00486 
00487   retval = _dbus_data_slot_list_set (&slot_allocator,
00488                                      &pending->slot_list,
00489                                      slot, data, free_data_func,
00490                                      &old_free_func, &old_data);
00491 
00492   /* Drop locks to call out to app code */
00493   CONNECTION_UNLOCK (pending->connection);
00494   
00495   if (retval)
00496     {
00497       if (old_free_func)
00498         (* old_free_func) (old_data);
00499     }
00500 
00501   CONNECTION_LOCK (pending->connection);
00502   
00503   return retval;
00504 }
00505 
00552 DBusPendingCall *
00553 dbus_pending_call_ref (DBusPendingCall *pending)
00554 {
00555   _dbus_return_val_if_fail (pending != NULL, NULL);
00556 
00557   /* The connection lock is better than the global
00558    * lock in the atomic increment fallback
00559    */
00560 #ifdef DBUS_HAVE_ATOMIC_INT
00561   _dbus_atomic_inc (&pending->refcount);
00562 #else
00563   CONNECTION_LOCK (pending->connection);
00564   _dbus_assert (pending->refcount.value > 0);
00565 
00566   pending->refcount.value += 1;
00567   CONNECTION_UNLOCK (pending->connection);
00568 #endif
00569   
00570   return pending;
00571 }
00572 
00579 void
00580 dbus_pending_call_unref (DBusPendingCall *pending)
00581 {
00582   dbus_bool_t last_unref;
00583 
00584   _dbus_return_if_fail (pending != NULL);
00585 
00586   /* More efficient to use the connection lock instead of atomic
00587    * int fallback if we lack atomic int decrement
00588    */
00589 #ifdef DBUS_HAVE_ATOMIC_INT
00590   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
00591 #else
00592   CONNECTION_LOCK (pending->connection);
00593   _dbus_assert (pending->refcount.value > 0);
00594   pending->refcount.value -= 1;
00595   last_unref = pending->refcount.value == 0;
00596   CONNECTION_UNLOCK (pending->connection);
00597 #endif
00598   
00599   if (last_unref)
00600     _dbus_pending_call_last_unref(pending);
00601 }
00602 
00613 dbus_bool_t
00614 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00615                               DBusPendingCallNotifyFunction function,
00616                               void                         *user_data,
00617                               DBusFreeFunction              free_user_data)
00618 {
00619   _dbus_return_val_if_fail (pending != NULL, FALSE);
00620 
00621   CONNECTION_LOCK (pending->connection);
00622   
00623   /* could invoke application code! */
00624   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00625                                              user_data, free_user_data))
00626     return FALSE;
00627   
00628   pending->function = function;
00629 
00630   CONNECTION_UNLOCK (pending->connection);
00631   
00632   return TRUE;
00633 }
00634 
00650 void
00651 dbus_pending_call_cancel (DBusPendingCall *pending)
00652 {
00653   _dbus_return_if_fail (pending != NULL);
00654 
00655   _dbus_connection_remove_pending_call (pending->connection,
00656                                         pending);
00657 }
00658 
00666 dbus_bool_t
00667 dbus_pending_call_get_completed (DBusPendingCall *pending)
00668 {
00669   dbus_bool_t completed;
00670   
00671   _dbus_return_val_if_fail (pending != NULL, FALSE);
00672 
00673   CONNECTION_LOCK (pending->connection);
00674   completed = pending->completed;
00675   CONNECTION_UNLOCK (pending->connection);
00676 
00677   return completed;
00678 }
00679 
00689 DBusMessage*
00690 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00691 {
00692   DBusMessage *message;
00693   
00694   _dbus_return_val_if_fail (pending != NULL, NULL);
00695   _dbus_return_val_if_fail (pending->completed, NULL);
00696   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00697 
00698   CONNECTION_LOCK (pending->connection);
00699   
00700   message = pending->reply;
00701   pending->reply = NULL;
00702 
00703   CONNECTION_UNLOCK (pending->connection);
00704   
00705   return message;
00706 }
00707 
00723 void
00724 dbus_pending_call_block (DBusPendingCall *pending)
00725 {
00726   _dbus_return_if_fail (pending != NULL);
00727 
00728   _dbus_connection_block_pending_call (pending);
00729 }
00730 
00745 dbus_bool_t
00746 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00747 {
00748   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00749 
00750   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00751                                           &_DBUS_LOCK_NAME (pending_call_slots),
00752                                           slot_p);
00753 }
00754 
00766 void
00767 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00768 {
00769   _dbus_return_if_fail (slot_p != NULL);
00770   _dbus_return_if_fail (*slot_p >= 0);
00771 
00772   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00773 }
00774 
00788 dbus_bool_t
00789 dbus_pending_call_set_data (DBusPendingCall  *pending,
00790                             dbus_int32_t      slot,
00791                             void             *data,
00792                             DBusFreeFunction  free_data_func)
00793 {
00794   dbus_bool_t retval;
00795   
00796   _dbus_return_val_if_fail (pending != NULL, FALSE);
00797   _dbus_return_val_if_fail (slot >= 0, FALSE);
00798 
00799   
00800   CONNECTION_LOCK (pending->connection);
00801   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00802   CONNECTION_UNLOCK (pending->connection);
00803   return retval;
00804 }
00805 
00814 void*
00815 dbus_pending_call_get_data (DBusPendingCall   *pending,
00816                             dbus_int32_t       slot)
00817 {
00818   void *res;
00819 
00820   _dbus_return_val_if_fail (pending != NULL, NULL);
00821 
00822   CONNECTION_LOCK (pending->connection);
00823   res = _dbus_data_slot_list_get (&slot_allocator,
00824                                   &pending->slot_list,
00825                                   slot);
00826   CONNECTION_UNLOCK (pending->connection);
00827 
00828   return res;
00829 }
00830 
00833 #ifdef DBUS_BUILD_TESTS
00834 
00841 dbus_bool_t
00842 _dbus_pending_call_test (const char *test_data_dir)
00843 {  
00844 
00845   return TRUE;
00846 }
00847 #endif /* DBUS_BUILD_TESTS */