D-Bus  1.6.8
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-message-internal.h"
00028 #include "dbus-pending-call-internal.h"
00029 #include "dbus-pending-call.h"
00030 #include "dbus-list.h"
00031 #include "dbus-threads.h"
00032 #include "dbus-test.h"
00033 
00053 #define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
00054 
00057 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
00058 
00062 struct DBusPendingCall
00063 {
00064   DBusAtomic refcount;                            
00066   DBusDataSlotList slot_list;                     
00068   DBusPendingCallNotifyFunction function;         
00070   DBusConnection *connection;                     
00071   DBusMessage *reply;                             
00072   DBusTimeout *timeout;                           
00074   DBusList *timeout_link;                         
00076   dbus_uint32_t reply_serial;                     
00078   unsigned int completed : 1;                     
00079   unsigned int timeout_added : 1;                 
00080 };
00081 
00082 #ifdef DBUS_ENABLE_VERBOSE_MODE
00083 static void
00084 _dbus_pending_call_trace_ref (DBusPendingCall *pending_call,
00085     int old_refcount,
00086     int new_refcount,
00087     const char *why)
00088 {
00089   static int enabled = -1;
00090 
00091   _dbus_trace_ref ("DBusPendingCall", pending_call, old_refcount,
00092       new_refcount, why, "DBUS_PENDING_CALL_TRACE", &enabled);
00093 }
00094 #else
00095 #define _dbus_pending_call_trace_ref(p, o, n, w) \
00096   do \
00097   {\
00098     (void) (o); \
00099     (void) (n); \
00100   } while (0)
00101 #endif
00102 
00103 static dbus_int32_t notify_user_data_slot = -1;
00104 
00115 DBusPendingCall*
00116 _dbus_pending_call_new_unlocked (DBusConnection    *connection,
00117                                  int                timeout_milliseconds,
00118                                  DBusTimeoutHandler timeout_handler)
00119 {
00120   DBusPendingCall *pending;
00121   DBusTimeout *timeout;
00122 
00123   _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
00124  
00125   if (timeout_milliseconds == -1)
00126     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
00127 
00128   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
00129     return NULL;
00130   
00131   pending = dbus_new0 (DBusPendingCall, 1);
00132   
00133   if (pending == NULL)
00134     {
00135       dbus_pending_call_free_data_slot (&notify_user_data_slot);
00136       return NULL;
00137     }
00138 
00139   if (timeout_milliseconds != DBUS_TIMEOUT_INFINITE)
00140     {
00141       timeout = _dbus_timeout_new (timeout_milliseconds,
00142                                    timeout_handler,
00143                                    pending, NULL);  
00144 
00145       if (timeout == NULL)
00146         {
00147           dbus_pending_call_free_data_slot (&notify_user_data_slot);
00148           dbus_free (pending);
00149           return NULL;
00150         }
00151 
00152       pending->timeout = timeout;
00153     }
00154   else
00155     {
00156       pending->timeout = NULL;
00157     }
00158 
00159   _dbus_atomic_inc (&pending->refcount);
00160   pending->connection = connection;
00161   _dbus_connection_ref_unlocked (pending->connection);
00162 
00163   _dbus_data_slot_list_init (&pending->slot_list);
00164 
00165   _dbus_pending_call_trace_ref (pending, 0, 1, "new_unlocked");
00166 
00167   return pending;
00168 }
00169 
00178 void
00179 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
00180                                        DBusMessage     *message)
00181 {
00182   if (message == NULL)
00183     {
00184       message = pending->timeout_link->data;
00185       _dbus_list_clear (&pending->timeout_link);
00186     }
00187   else
00188     dbus_message_ref (message);
00189 
00190   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
00191                  message,
00192                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
00193                  "method return" :
00194                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
00195                  "error" : "other type",
00196                  pending->reply_serial);
00197   
00198   _dbus_assert (pending->reply == NULL);
00199   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
00200   pending->reply = message;
00201 }
00202 
00210 void
00211 _dbus_pending_call_complete (DBusPendingCall *pending)
00212 {
00213   _dbus_assert (!pending->completed);
00214   
00215   pending->completed = TRUE;
00216 
00217   if (pending->function)
00218     {
00219       void *user_data;
00220       user_data = dbus_pending_call_get_data (pending,
00221                                               notify_user_data_slot);
00222       
00223       (* pending->function) (pending, user_data);
00224     }
00225 }
00226 
00234 void
00235 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
00236                                                  DBusConnection  *connection)
00237 {
00238   _dbus_assert (connection == pending->connection);
00239   
00240   if (pending->timeout_link)
00241     {
00242       _dbus_connection_queue_synthesized_message_link (connection,
00243                                                        pending->timeout_link);
00244       pending->timeout_link = NULL;
00245     }
00246 }
00247 
00254 dbus_bool_t 
00255 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
00256 {
00257   _dbus_assert (pending != NULL);
00258 
00259   return pending->timeout_added;
00260 }
00261 
00262 
00269 void
00270 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
00271                                                dbus_bool_t       is_added)
00272 {
00273   _dbus_assert (pending != NULL);
00274 
00275   pending->timeout_added = is_added;
00276 }
00277 
00278 
00285 DBusTimeout *
00286 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
00287 {
00288   _dbus_assert (pending != NULL);
00289 
00290   return pending->timeout;
00291 }
00292 
00299 dbus_uint32_t 
00300 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
00301 {
00302   _dbus_assert (pending != NULL);
00303 
00304   return pending->reply_serial;
00305 }
00306 
00313 void
00314 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
00315                                                dbus_uint32_t serial)
00316 {
00317   _dbus_assert (pending != NULL);
00318   _dbus_assert (pending->reply_serial == 0);
00319 
00320   pending->reply_serial = serial;
00321 }
00322 
00329 DBusConnection *
00330 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
00331 {
00332   _dbus_assert (pending != NULL);
00333  
00334   CONNECTION_LOCK (pending->connection);
00335   return pending->connection;
00336 }
00337 
00344 DBusConnection *
00345 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
00346 {
00347   _dbus_assert (pending != NULL);
00348  
00349   return pending->connection;
00350 }
00351 
00360 dbus_bool_t
00361 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
00362                                                DBusMessage     *message,
00363                                                dbus_uint32_t    serial)
00364 { 
00365   DBusList *reply_link;
00366   DBusMessage *reply;
00367 
00368   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
00369                                   "Did not receive a reply. Possible causes include: "
00370                                   "the remote application did not send a reply, "
00371                                   "the message bus security policy blocked the reply, "
00372                                   "the reply timeout expired, or "
00373                                   "the network connection was broken.");
00374   if (reply == NULL)
00375     return FALSE;
00376 
00377   reply_link = _dbus_list_alloc_link (reply);
00378   if (reply_link == NULL)
00379     {
00380       /* it's OK to unref this, nothing that could have attached a callback
00381        * has ever seen it */
00382       dbus_message_unref (reply);
00383       return FALSE;
00384     }
00385 
00386   pending->timeout_link = reply_link;
00387 
00388   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
00389   
00390   return TRUE;
00391 }
00392 
00400 DBusPendingCall *
00401 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
00402 {
00403   dbus_int32_t old_refcount;
00404 
00405   old_refcount = _dbus_atomic_inc (&pending->refcount);
00406   _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1,
00407       "ref_unlocked");
00408 
00409   return pending;
00410 }
00411 
00412 
00413 static void
00414 _dbus_pending_call_last_unref (DBusPendingCall *pending)
00415 {
00416   DBusConnection *connection;
00417   
00418   /* If we get here, we should be already detached
00419    * from the connection, or never attached.
00420    */
00421   _dbus_assert (!pending->timeout_added);  
00422 
00423   connection = pending->connection;
00424 
00425   /* this assumes we aren't holding connection lock... */
00426   _dbus_data_slot_list_free (&pending->slot_list);
00427 
00428   if (pending->timeout != NULL)
00429     _dbus_timeout_unref (pending->timeout);
00430       
00431   if (pending->timeout_link)
00432     {
00433       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
00434       _dbus_list_free_link (pending->timeout_link);
00435       pending->timeout_link = NULL;
00436     }
00437 
00438   if (pending->reply)
00439     {
00440       dbus_message_unref (pending->reply);
00441       pending->reply = NULL;
00442     }
00443       
00444   dbus_free (pending);
00445 
00446   dbus_pending_call_free_data_slot (&notify_user_data_slot);
00447 
00448   /* connection lock should not be held. */
00449   /* Free the connection last to avoid a weird state while
00450    * calling out to application code where the pending exists
00451    * but not the connection.
00452    */
00453   dbus_connection_unref (connection);
00454 }
00455 
00463 void
00464 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
00465 {
00466   dbus_int32_t old_refcount;
00467 
00468   old_refcount = _dbus_atomic_dec (&pending->refcount);
00469   _dbus_assert (old_refcount > 0);
00470   _dbus_pending_call_trace_ref (pending, old_refcount,
00471       old_refcount - 1, "unref_and_unlock");
00472 
00473   CONNECTION_UNLOCK (pending->connection);
00474 
00475   if (old_refcount == 1)
00476     _dbus_pending_call_last_unref (pending);
00477 }
00478 
00486 dbus_bool_t
00487 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
00488 {
00489   return pending->completed;
00490 }
00491 
00492 static DBusDataSlotAllocator slot_allocator;
00493 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
00494 
00508 dbus_bool_t
00509 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
00510                                      dbus_int32_t      slot,
00511                                      void             *data,
00512                                      DBusFreeFunction  free_data_func)
00513 {
00514   DBusFreeFunction old_free_func;
00515   void *old_data;
00516   dbus_bool_t retval;
00517 
00518   retval = _dbus_data_slot_list_set (&slot_allocator,
00519                                      &pending->slot_list,
00520                                      slot, data, free_data_func,
00521                                      &old_free_func, &old_data);
00522 
00523   /* Drop locks to call out to app code */
00524   CONNECTION_UNLOCK (pending->connection);
00525   
00526   if (retval)
00527     {
00528       if (old_free_func)
00529         (* old_free_func) (old_data);
00530     }
00531 
00532   CONNECTION_LOCK (pending->connection);
00533   
00534   return retval;
00535 }
00536 
00583 DBusPendingCall *
00584 dbus_pending_call_ref (DBusPendingCall *pending)
00585 {
00586   dbus_int32_t old_refcount;
00587 
00588   _dbus_return_val_if_fail (pending != NULL, NULL);
00589 
00590   old_refcount = _dbus_atomic_inc (&pending->refcount);
00591   _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1,
00592       "ref");
00593 
00594   return pending;
00595 }
00596 
00603 void
00604 dbus_pending_call_unref (DBusPendingCall *pending)
00605 {
00606   dbus_int32_t old_refcount;
00607 
00608   _dbus_return_if_fail (pending != NULL);
00609 
00610   old_refcount = _dbus_atomic_dec (&pending->refcount);
00611   _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount - 1,
00612       "unref");
00613 
00614   if (old_refcount == 1)
00615     _dbus_pending_call_last_unref(pending);
00616 }
00617 
00628 dbus_bool_t
00629 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00630                               DBusPendingCallNotifyFunction function,
00631                               void                         *user_data,
00632                               DBusFreeFunction              free_user_data)
00633 {
00634   dbus_bool_t ret = FALSE;
00635 
00636   _dbus_return_val_if_fail (pending != NULL, FALSE);
00637 
00638   CONNECTION_LOCK (pending->connection);
00639   
00640   /* could invoke application code! */
00641   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00642                                              user_data, free_user_data))
00643     goto out;
00644   
00645   pending->function = function;
00646   ret = TRUE;
00647 
00648 out:
00649   CONNECTION_UNLOCK (pending->connection);
00650   
00651   return ret;
00652 }
00653 
00669 void
00670 dbus_pending_call_cancel (DBusPendingCall *pending)
00671 {
00672   _dbus_return_if_fail (pending != NULL);
00673 
00674   _dbus_connection_remove_pending_call (pending->connection,
00675                                         pending);
00676 }
00677 
00685 dbus_bool_t
00686 dbus_pending_call_get_completed (DBusPendingCall *pending)
00687 {
00688   dbus_bool_t completed;
00689   
00690   _dbus_return_val_if_fail (pending != NULL, FALSE);
00691 
00692   CONNECTION_LOCK (pending->connection);
00693   completed = pending->completed;
00694   CONNECTION_UNLOCK (pending->connection);
00695 
00696   return completed;
00697 }
00698 
00708 DBusMessage*
00709 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00710 {
00711   DBusMessage *message;
00712   
00713   _dbus_return_val_if_fail (pending != NULL, NULL);
00714   _dbus_return_val_if_fail (pending->completed, NULL);
00715   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00716 
00717   CONNECTION_LOCK (pending->connection);
00718   
00719   message = pending->reply;
00720   pending->reply = NULL;
00721 
00722   CONNECTION_UNLOCK (pending->connection);
00723 
00724   _dbus_message_trace_ref (message, -1, -1, "dbus_pending_call_steal_reply");
00725   return message;
00726 }
00727 
00743 void
00744 dbus_pending_call_block (DBusPendingCall *pending)
00745 {
00746   _dbus_return_if_fail (pending != NULL);
00747 
00748   _dbus_connection_block_pending_call (pending);
00749 }
00750 
00765 dbus_bool_t
00766 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00767 {
00768   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00769 
00770   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00771                                           &_DBUS_LOCK_NAME (pending_call_slots),
00772                                           slot_p);
00773 }
00774 
00786 void
00787 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00788 {
00789   _dbus_return_if_fail (slot_p != NULL);
00790   _dbus_return_if_fail (*slot_p >= 0);
00791 
00792   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00793 }
00794 
00808 dbus_bool_t
00809 dbus_pending_call_set_data (DBusPendingCall  *pending,
00810                             dbus_int32_t      slot,
00811                             void             *data,
00812                             DBusFreeFunction  free_data_func)
00813 {
00814   dbus_bool_t retval;
00815   
00816   _dbus_return_val_if_fail (pending != NULL, FALSE);
00817   _dbus_return_val_if_fail (slot >= 0, FALSE);
00818 
00819   
00820   CONNECTION_LOCK (pending->connection);
00821   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00822   CONNECTION_UNLOCK (pending->connection);
00823   return retval;
00824 }
00825 
00834 void*
00835 dbus_pending_call_get_data (DBusPendingCall   *pending,
00836                             dbus_int32_t       slot)
00837 {
00838   void *res;
00839 
00840   _dbus_return_val_if_fail (pending != NULL, NULL);
00841 
00842   CONNECTION_LOCK (pending->connection);
00843   res = _dbus_data_slot_list_get (&slot_allocator,
00844                                   &pending->slot_list,
00845                                   slot);
00846   CONNECTION_UNLOCK (pending->connection);
00847 
00848   return res;
00849 }
00850