X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-pending-call.c;h=be5341058ad9b1d3a45259bc074370b86bd0dfa4;hb=bb8dd7fec5389db4df9b5e8863974149e8a650dc;hp=ba5f8757e17c054472e1d520456c57a6d2a2343c;hpb=d330ea7d1c98d29383dff2fc0a48269bde998760;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-pending-call.c b/dbus/dbus-pending-call.c index ba5f875..be53410 100644 --- a/dbus/dbus-pending-call.c +++ b/dbus/dbus-pending-call.c @@ -1,4 +1,4 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-pending-call.c Object representing a call in progress. * * Copyright (C) 2002, 2003 Red Hat Inc. @@ -17,12 +17,14 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include #include "dbus-internals.h" #include "dbus-connection-internal.h" +#include "dbus-message-internal.h" #include "dbus-pending-call-internal.h" #include "dbus-pending-call.h" #include "dbus-list.h" @@ -44,6 +46,19 @@ * * Opaque object representing a reply message that we're waiting for. */ + +/** + * shorter and more visible way to write _dbus_connection_lock() + */ +#define CONNECTION_LOCK(connection) _dbus_connection_lock(connection) +/** + * shorter and more visible way to write _dbus_connection_unlock() + */ +#define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection) + +/** + * Implementation details of #DBusPendingCall - all fields are private. + */ struct DBusPendingCall { DBusAtomic refcount; /**< reference count */ @@ -64,20 +79,36 @@ struct DBusPendingCall unsigned int timeout_added : 1; /**< Have added the timeout */ }; +static void +_dbus_pending_call_trace_ref (DBusPendingCall *pending_call, + int old_refcount, + int new_refcount, + const char *why) +{ +#ifdef DBUS_ENABLE_VERBOSE_MODE + static int enabled = -1; + + _dbus_trace_ref ("DBusPendingCall", pending_call, old_refcount, + new_refcount, why, "DBUS_PENDING_CALL_TRACE", &enabled); +#endif +} + static dbus_int32_t notify_user_data_slot = -1; /** * Creates a new pending reply object. * * @param connection connection where reply will arrive - * @param timeout_milliseconds length of timeout, -1 for default + * @param timeout_milliseconds length of timeout, -1 (or + * #DBUS_TIMEOUT_USE_DEFAULT) for default, + * #DBUS_TIMEOUT_INFINITE for no timeout * @param timeout_handler timeout handler, takes pending call as data * @returns a new #DBusPendingCall or #NULL if no memory. */ DBusPendingCall* -_dbus_pending_call_new (DBusConnection *connection, - int timeout_milliseconds, - DBusTimeoutHandler timeout_handler) +_dbus_pending_call_new_unlocked (DBusConnection *connection, + int timeout_milliseconds, + DBusTimeoutHandler timeout_handler) { DBusPendingCall *pending; DBusTimeout *timeout; @@ -87,14 +118,6 @@ _dbus_pending_call_new (DBusConnection *connection, if (timeout_milliseconds == -1) timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE; - /* it would probably seem logical to pass in _DBUS_INT_MAX for - * infinite timeout, but then math in - * _dbus_connection_block_for_reply would get all overflow-prone, so - * smack that down. - */ - if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6) - timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6; - if (!dbus_pending_call_allocate_data_slot (¬ify_user_data_slot)) return NULL; @@ -106,26 +129,34 @@ _dbus_pending_call_new (DBusConnection *connection, return NULL; } - timeout = _dbus_timeout_new (timeout_milliseconds, - timeout_handler, - pending, NULL); + if (timeout_milliseconds != DBUS_TIMEOUT_INFINITE) + { + timeout = _dbus_timeout_new (timeout_milliseconds, + timeout_handler, + pending, NULL); - if (timeout == NULL) + if (timeout == NULL) + { + dbus_pending_call_free_data_slot (¬ify_user_data_slot); + dbus_free (pending); + return NULL; + } + + pending->timeout = timeout; + } + else { - dbus_pending_call_free_data_slot (¬ify_user_data_slot); - dbus_free (pending); - return NULL; + pending->timeout = NULL; } - - pending->refcount.value = 1; + + _dbus_atomic_inc (&pending->refcount); pending->connection = connection; - dbus_connection_ref (pending->connection); + _dbus_connection_ref_unlocked (pending->connection); - pending->timeout = timeout; + _dbus_data_slot_list_init (&pending->slot_list); + _dbus_pending_call_trace_ref (pending, 0, 1, "new_unlocked"); - _dbus_data_slot_list_init (&pending->slot_list); - return pending; } @@ -138,8 +169,8 @@ _dbus_pending_call_new (DBusConnection *connection, * to time out the call */ void -_dbus_pending_call_set_reply (DBusPendingCall *pending, - DBusMessage *message) +_dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending, + DBusMessage *message) { if (message == NULL) { @@ -186,10 +217,19 @@ _dbus_pending_call_complete (DBusPendingCall *pending) } } +/** + * If the pending call hasn't been timed out, add its timeout + * error reply to the connection's incoming message queue. + * + * @param pending the pending call + * @param connection the connection the call was sent to + */ void -_dbus_pending_call_queue_timeout_error (DBusPendingCall *pending, - DBusConnection *connection) +_dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, + DBusConnection *connection) { + _dbus_assert (connection == pending->connection); + if (pending->timeout_link) { _dbus_connection_queue_synthesized_message_link (connection, @@ -205,7 +245,7 @@ _dbus_pending_call_queue_timeout_error (DBusPendingCall *pending, * @returns #TRUE if there is a timeout or #FALSE if not */ dbus_bool_t -_dbus_pending_call_is_timeout_added (DBusPendingCall *pending) +_dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall *pending) { _dbus_assert (pending != NULL); @@ -220,8 +260,8 @@ _dbus_pending_call_is_timeout_added (DBusPendingCall *pending) * @param is_added whether or not a timeout is added */ void -_dbus_pending_call_set_timeout_added (DBusPendingCall *pending, - dbus_bool_t is_added) +_dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall *pending, + dbus_bool_t is_added) { _dbus_assert (pending != NULL); @@ -233,10 +273,10 @@ _dbus_pending_call_set_timeout_added (DBusPendingCall *pending, * Retrives the timeout * * @param pending the pending_call - * @returns a timeout object + * @returns a timeout object or NULL if call has no timeout */ DBusTimeout * -_dbus_pending_call_get_timeout (DBusPendingCall *pending) +_dbus_pending_call_get_timeout_unlocked (DBusPendingCall *pending) { _dbus_assert (pending != NULL); @@ -250,7 +290,7 @@ _dbus_pending_call_get_timeout (DBusPendingCall *pending) * @returns a serial number for the reply or 0 */ dbus_uint32_t -_dbus_pending_call_get_reply_serial (DBusPendingCall *pending) +_dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall *pending) { _dbus_assert (pending != NULL); @@ -264,8 +304,8 @@ _dbus_pending_call_get_reply_serial (DBusPendingCall *pending) * @param serial the serial number */ void -_dbus_pending_call_set_reply_serial (DBusPendingCall *pending, - dbus_uint32_t serial) +_dbus_pending_call_set_reply_serial_unlocked (DBusPendingCall *pending, + dbus_uint32_t serial) { _dbus_assert (pending != NULL); _dbus_assert (pending->reply_serial == 0); @@ -274,17 +314,32 @@ _dbus_pending_call_set_reply_serial (DBusPendingCall *pending, } /** - * Gets the connection associated with this pending call + * Gets the connection associated with this pending call. + * + * @param pending the pending_call + * @returns the connection associated with the pending call + */ +DBusConnection * +_dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending) +{ + _dbus_assert (pending != NULL); + + CONNECTION_LOCK (pending->connection); + return pending->connection; +} + +/** + * Gets the connection associated with this pending call. * * @param pending the pending_call * @returns the connection associated with the pending call */ DBusConnection * -_dbus_pending_call_get_connection (DBusPendingCall *pending) +_dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending) { _dbus_assert (pending != NULL); - return pending->connection; + return pending->connection; } /** @@ -296,9 +351,9 @@ _dbus_pending_call_get_connection (DBusPendingCall *pending) * @return #FALSE on OOM */ dbus_bool_t -_dbus_pending_call_set_timeout_error (DBusPendingCall *pending, - DBusMessage *message, - dbus_uint32_t serial) +_dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending, + DBusMessage *message, + dbus_uint32_t serial) { DBusList *reply_link; DBusMessage *reply; @@ -315,17 +370,163 @@ _dbus_pending_call_set_timeout_error (DBusPendingCall *pending, reply_link = _dbus_list_alloc_link (reply); if (reply_link == NULL) { + /* it's OK to unref this, nothing that could have attached a callback + * has ever seen it */ dbus_message_unref (reply); return FALSE; } pending->timeout_link = reply_link; - _dbus_pending_call_set_reply_serial (pending, serial); + _dbus_pending_call_set_reply_serial_unlocked (pending, serial); return TRUE; } +/** + * Increments the reference count on a pending call, + * while the lock on its connection is already held. + * + * @param pending the pending call object + * @returns the pending call object + */ +DBusPendingCall * +_dbus_pending_call_ref_unlocked (DBusPendingCall *pending) +{ + dbus_int32_t old_refcount; + + old_refcount = _dbus_atomic_inc (&pending->refcount); + _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1, + "ref_unlocked"); + + return pending; +} + + +static void +_dbus_pending_call_last_unref (DBusPendingCall *pending) +{ + DBusConnection *connection; + + /* If we get here, we should be already detached + * from the connection, or never attached. + */ + _dbus_assert (!pending->timeout_added); + + connection = pending->connection; + + /* this assumes we aren't holding connection lock... */ + _dbus_data_slot_list_free (&pending->slot_list); + + if (pending->timeout != NULL) + _dbus_timeout_unref (pending->timeout); + + if (pending->timeout_link) + { + dbus_message_unref ((DBusMessage *)pending->timeout_link->data); + _dbus_list_free_link (pending->timeout_link); + pending->timeout_link = NULL; + } + + if (pending->reply) + { + dbus_message_unref (pending->reply); + pending->reply = NULL; + } + + dbus_free (pending); + + dbus_pending_call_free_data_slot (¬ify_user_data_slot); + + /* connection lock should not be held. */ + /* Free the connection last to avoid a weird state while + * calling out to application code where the pending exists + * but not the connection. + */ + dbus_connection_unref (connection); +} + +/** + * Decrements the reference count on a pending call, + * freeing it if the count reaches 0. Assumes + * connection lock is already held. + * + * @param pending the pending call object + */ +void +_dbus_pending_call_unref_and_unlock (DBusPendingCall *pending) +{ + dbus_int32_t old_refcount; + + old_refcount = _dbus_atomic_dec (&pending->refcount); + _dbus_assert (old_refcount > 0); + _dbus_pending_call_trace_ref (pending, old_refcount, + old_refcount - 1, "unref_and_unlock"); + + CONNECTION_UNLOCK (pending->connection); + + if (old_refcount == 1) + _dbus_pending_call_last_unref (pending); +} + +/** + * Checks whether the pending call has received a reply + * yet, or not. Assumes connection lock is held. + * + * @param pending the pending call + * @returns #TRUE if a reply has been received + */ +dbus_bool_t +_dbus_pending_call_get_completed_unlocked (DBusPendingCall *pending) +{ + return pending->completed; +} + +static DBusDataSlotAllocator slot_allocator = + _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (pending_call_slots)); + +/** + * Stores a pointer on a #DBusPendingCall, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the pending call is finalized. The slot number + * must have been allocated with dbus_pending_call_allocate_data_slot(). + * + * @param pending the pending_call + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +_dbus_pending_call_set_data_unlocked (DBusPendingCall *pending, + dbus_int32_t slot, + void *data, + DBusFreeFunction free_data_func) +{ + DBusFreeFunction old_free_func; + void *old_data; + dbus_bool_t retval; + + retval = _dbus_data_slot_list_set (&slot_allocator, + &pending->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + + /* Drop locks to call out to app code */ + CONNECTION_UNLOCK (pending->connection); + + if (retval) + { + if (old_free_func) + (* old_free_func) (old_data); + } + + CONNECTION_LOCK (pending->connection); + + return retval; +} + /** @} */ /** @@ -341,6 +542,26 @@ _dbus_pending_call_set_timeout_error (DBusPendingCall *pending, */ /** + * @def DBUS_TIMEOUT_INFINITE + * + * An integer constant representing an infinite timeout. This has the + * numeric value 0x7fffffff (the largest 32-bit signed integer). + * + * For source compatibility with D-Bus versions earlier than 1.4.12, use + * 0x7fffffff, or INT32_MAX (assuming your platform has it). + */ + +/** + * @def DBUS_TIMEOUT_USE_DEFAULT + * + * An integer constant representing a request to use the default timeout. + * This has numeric value -1. + * + * For source compatibility with D-Bus versions earlier than 1.4.12, use a + * literal -1. + */ + +/** * @typedef DBusPendingCall * * Opaque data type representing a message pending. @@ -355,9 +576,13 @@ _dbus_pending_call_set_timeout_error (DBusPendingCall *pending, DBusPendingCall * dbus_pending_call_ref (DBusPendingCall *pending) { + dbus_int32_t old_refcount; + _dbus_return_val_if_fail (pending != NULL, NULL); - _dbus_atomic_inc (&pending->refcount); + old_refcount = _dbus_atomic_inc (&pending->refcount); + _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1, + "ref"); return pending; } @@ -371,44 +596,16 @@ dbus_pending_call_ref (DBusPendingCall *pending) void dbus_pending_call_unref (DBusPendingCall *pending) { - dbus_bool_t last_unref; + dbus_int32_t old_refcount; _dbus_return_if_fail (pending != NULL); - last_unref = (_dbus_atomic_dec (&pending->refcount) == 1); + old_refcount = _dbus_atomic_dec (&pending->refcount); + _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount - 1, + "unref"); - if (last_unref) - { - /* If we get here, we should be already detached - * from the connection, or never attached. - */ - _dbus_assert (!pending->timeout_added); - - dbus_connection_unref (pending->connection); - - /* this assumes we aren't holding connection lock... */ - _dbus_data_slot_list_free (&pending->slot_list); - - if (pending->timeout != NULL) - _dbus_timeout_unref (pending->timeout); - - if (pending->timeout_link) - { - dbus_message_unref ((DBusMessage *)pending->timeout_link->data); - _dbus_list_free_link (pending->timeout_link); - pending->timeout_link = NULL; - } - - if (pending->reply) - { - dbus_message_unref (pending->reply); - pending->reply = NULL; - } - - dbus_free (pending); - - dbus_pending_call_free_data_slot (¬ify_user_data_slot); - } + if (old_refcount == 1) + _dbus_pending_call_last_unref(pending); } /** @@ -427,47 +624,69 @@ dbus_pending_call_set_notify (DBusPendingCall *pending, void *user_data, DBusFreeFunction free_user_data) { + dbus_bool_t ret = FALSE; + _dbus_return_val_if_fail (pending != NULL, FALSE); + CONNECTION_LOCK (pending->connection); + /* could invoke application code! */ - if (!dbus_pending_call_set_data (pending, notify_user_data_slot, - user_data, free_user_data)) - return FALSE; + if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot, + user_data, free_user_data)) + goto out; pending->function = function; + ret = TRUE; - return TRUE; +out: + CONNECTION_UNLOCK (pending->connection); + + return ret; } /** * Cancels the pending call, such that any reply or error received * will just be ignored. Drops the dbus library's internal reference * to the #DBusPendingCall so will free the call if nobody else is - * holding a reference. However you usually get a reference - * from dbus_connection_send() so probably your app owns a ref also. + * holding a reference. However you usually get a reference from + * dbus_connection_send_with_reply() so probably your app owns a ref + * also. + * + * Note that canceling a pending call will not simulate a + * timed-out call; if a call times out, then a timeout error reply is + * received. If you cancel the call, no reply is received unless the + * the reply was already received before you canceled. * * @param pending the pending call */ void dbus_pending_call_cancel (DBusPendingCall *pending) { - if (pending->connection) - _dbus_connection_remove_pending_call (pending->connection, - pending); + _dbus_return_if_fail (pending != NULL); + + _dbus_connection_remove_pending_call (pending->connection, + pending); } /** * Checks whether the pending call has received a reply * yet, or not. * - * @todo not thread safe? I guess it has to lock though it sucks - * * @param pending the pending call - * @returns #TRUE if a reply has been received */ + * @returns #TRUE if a reply has been received + */ dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall *pending) { - return pending->completed; + dbus_bool_t completed; + + _dbus_return_val_if_fail (pending != NULL, FALSE); + + CONNECTION_LOCK (pending->connection); + completed = pending->completed; + CONNECTION_UNLOCK (pending->connection); + + return completed; } /** @@ -484,12 +703,18 @@ dbus_pending_call_steal_reply (DBusPendingCall *pending) { DBusMessage *message; + _dbus_return_val_if_fail (pending != NULL, NULL); _dbus_return_val_if_fail (pending->completed, NULL); _dbus_return_val_if_fail (pending->reply != NULL, NULL); + + CONNECTION_LOCK (pending->connection); message = pending->reply; pending->reply = NULL; + CONNECTION_UNLOCK (pending->connection); + + _dbus_message_trace_ref (message, -1, -1, "dbus_pending_call_steal_reply"); return message; } @@ -504,18 +729,18 @@ dbus_pending_call_steal_reply (DBusPendingCall *pending) * * @todo when you start blocking, the timeout is reset, but it should * really only use time remaining since the pending call was created. + * This requires storing timestamps instead of intervals in the timeout * * @param pending the pending call */ void dbus_pending_call_block (DBusPendingCall *pending) { + _dbus_return_if_fail (pending != NULL); + _dbus_connection_block_pending_call (pending); } -static DBusDataSlotAllocator slot_allocator; -_DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots); - /** * Allocates an integer ID to be used for storing application-specific * data on any DBusPendingCall. The allocated ID may then be used @@ -533,8 +758,9 @@ _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots); dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p) { + _dbus_return_val_if_fail (slot_p != NULL, FALSE); + return _dbus_data_slot_allocator_alloc (&slot_allocator, - _DBUS_LOCK_NAME (pending_call_slots), slot_p); } @@ -552,8 +778,9 @@ dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p) void dbus_pending_call_free_data_slot (dbus_int32_t *slot_p) { + _dbus_return_if_fail (slot_p != NULL); _dbus_return_if_fail (*slot_p >= 0); - + _dbus_data_slot_allocator_free (&slot_allocator, slot_p); } @@ -576,24 +803,15 @@ dbus_pending_call_set_data (DBusPendingCall *pending, void *data, DBusFreeFunction free_data_func) { - DBusFreeFunction old_free_func; - void *old_data; dbus_bool_t retval; - + _dbus_return_val_if_fail (pending != NULL, FALSE); _dbus_return_val_if_fail (slot >= 0, FALSE); - retval = _dbus_data_slot_list_set (&slot_allocator, - &pending->slot_list, - slot, data, free_data_func, - &old_free_func, &old_data); - - if (retval) - { - if (old_free_func) - (* old_free_func) (old_data); - } - + + CONNECTION_LOCK (pending->connection); + retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func); + CONNECTION_UNLOCK (pending->connection); return retval; } @@ -613,27 +831,13 @@ dbus_pending_call_get_data (DBusPendingCall *pending, _dbus_return_val_if_fail (pending != NULL, NULL); + CONNECTION_LOCK (pending->connection); res = _dbus_data_slot_list_get (&slot_allocator, &pending->slot_list, slot); + CONNECTION_UNLOCK (pending->connection); return res; } /** @} */ - -#ifdef DBUS_BUILD_TESTS - -/** - * @ingroup DBusPendingCallInternals - * Unit test for DBusPendingCall. - * - * @returns #TRUE on success. - */ -dbus_bool_t -_dbus_pending_call_test (const char *test_data_dir) -{ - - return TRUE; -} -#endif /* DBUS_BUILD_TESTS */