D-Bus
1.6.8
|
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 (¬ify_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 (¬ify_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 (¬ify_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 (¬ify_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