D-Bus
1.4.18
|
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 (¬ify_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 (¬ify_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 (¬ify_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 _dbus_atomic_inc (&pending->refcount); 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 _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 (¬ify_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 00551 DBusPendingCall * 00552 dbus_pending_call_ref (DBusPendingCall *pending) 00553 { 00554 _dbus_return_val_if_fail (pending != NULL, NULL); 00555 00556 _dbus_atomic_inc (&pending->refcount); 00557 00558 return pending; 00559 } 00560 00567 void 00568 dbus_pending_call_unref (DBusPendingCall *pending) 00569 { 00570 dbus_bool_t last_unref; 00571 00572 _dbus_return_if_fail (pending != NULL); 00573 00574 last_unref = (_dbus_atomic_dec (&pending->refcount) == 1); 00575 00576 if (last_unref) 00577 _dbus_pending_call_last_unref(pending); 00578 } 00579 00590 dbus_bool_t 00591 dbus_pending_call_set_notify (DBusPendingCall *pending, 00592 DBusPendingCallNotifyFunction function, 00593 void *user_data, 00594 DBusFreeFunction free_user_data) 00595 { 00596 _dbus_return_val_if_fail (pending != NULL, FALSE); 00597 00598 CONNECTION_LOCK (pending->connection); 00599 00600 /* could invoke application code! */ 00601 if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot, 00602 user_data, free_user_data)) 00603 return FALSE; 00604 00605 pending->function = function; 00606 00607 CONNECTION_UNLOCK (pending->connection); 00608 00609 return TRUE; 00610 } 00611 00627 void 00628 dbus_pending_call_cancel (DBusPendingCall *pending) 00629 { 00630 _dbus_return_if_fail (pending != NULL); 00631 00632 _dbus_connection_remove_pending_call (pending->connection, 00633 pending); 00634 } 00635 00643 dbus_bool_t 00644 dbus_pending_call_get_completed (DBusPendingCall *pending) 00645 { 00646 dbus_bool_t completed; 00647 00648 _dbus_return_val_if_fail (pending != NULL, FALSE); 00649 00650 CONNECTION_LOCK (pending->connection); 00651 completed = pending->completed; 00652 CONNECTION_UNLOCK (pending->connection); 00653 00654 return completed; 00655 } 00656 00666 DBusMessage* 00667 dbus_pending_call_steal_reply (DBusPendingCall *pending) 00668 { 00669 DBusMessage *message; 00670 00671 _dbus_return_val_if_fail (pending != NULL, NULL); 00672 _dbus_return_val_if_fail (pending->completed, NULL); 00673 _dbus_return_val_if_fail (pending->reply != NULL, NULL); 00674 00675 CONNECTION_LOCK (pending->connection); 00676 00677 message = pending->reply; 00678 pending->reply = NULL; 00679 00680 CONNECTION_UNLOCK (pending->connection); 00681 00682 return message; 00683 } 00684 00700 void 00701 dbus_pending_call_block (DBusPendingCall *pending) 00702 { 00703 _dbus_return_if_fail (pending != NULL); 00704 00705 _dbus_connection_block_pending_call (pending); 00706 } 00707 00722 dbus_bool_t 00723 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p) 00724 { 00725 _dbus_return_val_if_fail (slot_p != NULL, FALSE); 00726 00727 return _dbus_data_slot_allocator_alloc (&slot_allocator, 00728 &_DBUS_LOCK_NAME (pending_call_slots), 00729 slot_p); 00730 } 00731 00743 void 00744 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p) 00745 { 00746 _dbus_return_if_fail (slot_p != NULL); 00747 _dbus_return_if_fail (*slot_p >= 0); 00748 00749 _dbus_data_slot_allocator_free (&slot_allocator, slot_p); 00750 } 00751 00765 dbus_bool_t 00766 dbus_pending_call_set_data (DBusPendingCall *pending, 00767 dbus_int32_t slot, 00768 void *data, 00769 DBusFreeFunction free_data_func) 00770 { 00771 dbus_bool_t retval; 00772 00773 _dbus_return_val_if_fail (pending != NULL, FALSE); 00774 _dbus_return_val_if_fail (slot >= 0, FALSE); 00775 00776 00777 CONNECTION_LOCK (pending->connection); 00778 retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func); 00779 CONNECTION_UNLOCK (pending->connection); 00780 return retval; 00781 } 00782 00791 void* 00792 dbus_pending_call_get_data (DBusPendingCall *pending, 00793 dbus_int32_t slot) 00794 { 00795 void *res; 00796 00797 _dbus_return_val_if_fail (pending != NULL, NULL); 00798 00799 CONNECTION_LOCK (pending->connection); 00800 res = _dbus_data_slot_list_get (&slot_allocator, 00801 &pending->slot_list, 00802 slot); 00803 CONNECTION_UNLOCK (pending->connection); 00804 00805 return res; 00806 } 00807 00810 #ifdef DBUS_BUILD_TESTS 00811 00818 dbus_bool_t 00819 _dbus_pending_call_test (const char *test_data_dir) 00820 { 00821 00822 return TRUE; 00823 } 00824 #endif /* DBUS_BUILD_TESTS */