• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List

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   _dbus_atomic_inc (&pending->refcount);
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       /* it's OK to unref this, nothing that could have attached a callback
00355        * has ever seen it */
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   _dbus_atomic_inc (&pending->refcount);
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_int32_t old_refcount;
00437 
00438   old_refcount = _dbus_atomic_dec (&pending->refcount);
00439   _dbus_assert (old_refcount > 0);
00440 
00441   CONNECTION_UNLOCK (pending->connection);
00442 
00443   if (old_refcount == 1)
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   _dbus_atomic_inc (&pending->refcount);
00537 
00538   return pending;
00539 }
00540 
00547 void
00548 dbus_pending_call_unref (DBusPendingCall *pending)
00549 {
00550   dbus_bool_t last_unref;
00551 
00552   _dbus_return_if_fail (pending != NULL);
00553 
00554   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
00555 
00556   if (last_unref)
00557     _dbus_pending_call_last_unref(pending);
00558 }
00559 
00570 dbus_bool_t
00571 dbus_pending_call_set_notify (DBusPendingCall              *pending,
00572                               DBusPendingCallNotifyFunction function,
00573                               void                         *user_data,
00574                               DBusFreeFunction              free_user_data)
00575 {
00576   _dbus_return_val_if_fail (pending != NULL, FALSE);
00577 
00578   CONNECTION_LOCK (pending->connection);
00579   
00580   /* could invoke application code! */
00581   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
00582                                              user_data, free_user_data))
00583     return FALSE;
00584   
00585   pending->function = function;
00586 
00587   CONNECTION_UNLOCK (pending->connection);
00588   
00589   return TRUE;
00590 }
00591 
00607 void
00608 dbus_pending_call_cancel (DBusPendingCall *pending)
00609 {
00610   _dbus_return_if_fail (pending != NULL);
00611 
00612   _dbus_connection_remove_pending_call (pending->connection,
00613                                         pending);
00614 }
00615 
00623 dbus_bool_t
00624 dbus_pending_call_get_completed (DBusPendingCall *pending)
00625 {
00626   dbus_bool_t completed;
00627   
00628   _dbus_return_val_if_fail (pending != NULL, FALSE);
00629 
00630   CONNECTION_LOCK (pending->connection);
00631   completed = pending->completed;
00632   CONNECTION_UNLOCK (pending->connection);
00633 
00634   return completed;
00635 }
00636 
00646 DBusMessage*
00647 dbus_pending_call_steal_reply (DBusPendingCall *pending)
00648 {
00649   DBusMessage *message;
00650   
00651   _dbus_return_val_if_fail (pending != NULL, NULL);
00652   _dbus_return_val_if_fail (pending->completed, NULL);
00653   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
00654 
00655   CONNECTION_LOCK (pending->connection);
00656   
00657   message = pending->reply;
00658   pending->reply = NULL;
00659 
00660   CONNECTION_UNLOCK (pending->connection);
00661   
00662   return message;
00663 }
00664 
00680 void
00681 dbus_pending_call_block (DBusPendingCall *pending)
00682 {
00683   _dbus_return_if_fail (pending != NULL);
00684 
00685   _dbus_connection_block_pending_call (pending);
00686 }
00687 
00702 dbus_bool_t
00703 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
00704 {
00705   _dbus_return_val_if_fail (slot_p != NULL, FALSE);
00706 
00707   return _dbus_data_slot_allocator_alloc (&slot_allocator,
00708                                           &_DBUS_LOCK_NAME (pending_call_slots),
00709                                           slot_p);
00710 }
00711 
00723 void
00724 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
00725 {
00726   _dbus_return_if_fail (slot_p != NULL);
00727   _dbus_return_if_fail (*slot_p >= 0);
00728 
00729   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
00730 }
00731 
00745 dbus_bool_t
00746 dbus_pending_call_set_data (DBusPendingCall  *pending,
00747                             dbus_int32_t      slot,
00748                             void             *data,
00749                             DBusFreeFunction  free_data_func)
00750 {
00751   dbus_bool_t retval;
00752   
00753   _dbus_return_val_if_fail (pending != NULL, FALSE);
00754   _dbus_return_val_if_fail (slot >= 0, FALSE);
00755 
00756   
00757   CONNECTION_LOCK (pending->connection);
00758   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
00759   CONNECTION_UNLOCK (pending->connection);
00760   return retval;
00761 }
00762 
00771 void*
00772 dbus_pending_call_get_data (DBusPendingCall   *pending,
00773                             dbus_int32_t       slot)
00774 {
00775   void *res;
00776 
00777   _dbus_return_val_if_fail (pending != NULL, NULL);
00778 
00779   CONNECTION_LOCK (pending->connection);
00780   res = _dbus_data_slot_list_get (&slot_allocator,
00781                                   &pending->slot_list,
00782                                   slot);
00783   CONNECTION_UNLOCK (pending->connection);
00784 
00785   return res;
00786 }
00787 
00790 #ifdef DBUS_BUILD_TESTS
00791 
00798 dbus_bool_t
00799 _dbus_pending_call_test (const char *test_data_dir)
00800 {  
00801 
00802   return TRUE;
00803 }
00804 #endif /* DBUS_BUILD_TESTS */

Generated on Fri Jul 11 2014 20:41:48 for D-Bus by  doxygen 1.7.1