D-Bus 1.4.1
|
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 (¬ify_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 (¬ify_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 (¬ify_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 (¬ify_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 */