D-Bus 1.4.0

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