#include "dbus-protocol.h"
#include "dbus-dataslot.h"
#include "dbus-string.h"
+#include "dbus-signature.h"
#include "dbus-pending-call.h"
#include "dbus-object-tree.h"
#include "dbus-threads-internal.h"
#define TRACE_LOCKS 1
#define CONNECTION_LOCK(connection) do { \
- if (TRACE_LOCKS) { _dbus_verbose (" LOCK: %s\n", _DBUS_FUNCTION_NAME); } \
+ if (TRACE_LOCKS) { _dbus_verbose ("LOCK\n"); } \
_dbus_mutex_lock ((connection)->mutex); \
TOOK_LOCK_CHECK (connection); \
} while (0)
#define CONNECTION_UNLOCK(connection) do { \
- if (TRACE_LOCKS) { _dbus_verbose (" UNLOCK: %s\n", _DBUS_FUNCTION_NAME); } \
+ if (TRACE_LOCKS) { _dbus_verbose ("UNLOCK\n"); } \
RELEASING_LOCK_CHECK (connection); \
_dbus_mutex_unlock ((connection)->mutex); \
} while (0)
+#define SLOTS_LOCK(connection) do { \
+ _dbus_mutex_lock ((connection)->slot_mutex); \
+ } while (0)
+
+#define SLOTS_UNLOCK(connection) do { \
+ _dbus_mutex_unlock ((connection)->slot_mutex); \
+ } while (0)
+
#define DISPATCH_STATUS_NAME(s) \
((s) == DBUS_DISPATCH_COMPLETE ? "complete" : \
(s) == DBUS_DISPATCH_DATA_REMAINS ? "data remains" : \
DBusList *counter_link; /**< Preallocated link in the resource counter */
};
-#ifdef HAVE_DECL_MSG_NOSIGNAL
+#if HAVE_DECL_MSG_NOSIGNAL
static dbus_bool_t _dbus_modify_sigpipe = FALSE;
#else
static dbus_bool_t _dbus_modify_sigpipe = TRUE;
DBusList *filter_list; /**< List of filters. */
+ DBusMutex *slot_mutex; /**< Lock on slot_list so overall connection lock need not be taken */
DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
DBusHashTable *pending_replies; /**< Hash of message serials to #DBusPendingCall. */
static DBusDispatchStatus _dbus_connection_flush_unlocked (DBusConnection *connection);
static void _dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection);
static dbus_bool_t _dbus_connection_get_is_connected_unlocked (DBusConnection *connection);
+static dbus_bool_t _dbus_connection_peek_for_reply_unlocked (DBusConnection *connection,
+ dbus_uint32_t client_serial);
static DBusMessageFilter *
_dbus_message_filter_ref (DBusMessageFilter *filter)
_dbus_connection_wakeup_mainloop (connection);
- _dbus_verbose ("Message %p (%d %s %s %s '%s' reply to %u) added to incoming queue %p, %d incoming\n",
+ _dbus_verbose ("Message %p (%s %s %s %s '%s' reply to %u) added to incoming queue %p, %d incoming\n",
message,
- dbus_message_get_type (message),
+ dbus_message_type_to_string (dbus_message_get_type (message)),
dbus_message_get_path (message) ?
dbus_message_get_path (message) :
"no path",
connection->n_outgoing -= 1;
- _dbus_verbose ("Message %p (%d %s %s %s '%s') removed from outgoing queue %p, %d left to send\n",
+ _dbus_verbose ("Message %p (%s %s %s %s '%s') removed from outgoing queue %p, %d left to send\n",
message,
- dbus_message_get_type (message),
+ dbus_message_type_to_string (dbus_message_get_type (message)),
dbus_message_get_path (message) ?
dbus_message_get_path (message) :
"no path",
DBusWatchToggleFunction toggle_function,
dbus_bool_t enabled)
{
- DBusWatchList *watches;
dbus_bool_t retval;
-
+
HAVE_LOCK_CHECK (connection);
- /* This isn't really safe or reasonable; a better pattern is the "do everything, then
- * drop lock and call out" one; but it has to be propagated up through all callers
+ /* The original purpose of protected_change_watch() was to hold a
+ * ref on the connection while dropping the connection lock, then
+ * calling out to the app. This was a broken hack that did not
+ * work, since the connection was in a hosed state (no WatchList
+ * field) while calling out.
+ *
+ * So for now we'll just keep the lock while calling out. This means
+ * apps are not allowed to call DBusConnection methods inside a
+ * watch function or they will deadlock.
+ *
+ * The "real fix" is to use the _and_unlock() pattern found
+ * elsewhere in the code, to defer calling out to the app until
+ * we're about to drop locks and return flow of control to the app
+ * anyway.
+ *
+ * See http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
*/
-
- watches = connection->watches;
- if (watches)
- {
- connection->watches = NULL;
- _dbus_connection_ref_unlocked (connection);
- CONNECTION_UNLOCK (connection);
+ if (connection->watches)
+ {
if (add_function)
- retval = (* add_function) (watches, watch);
+ retval = (* add_function) (connection->watches, watch);
else if (remove_function)
{
retval = TRUE;
- (* remove_function) (watches, watch);
+ (* remove_function) (connection->watches, watch);
}
else
{
retval = TRUE;
- (* toggle_function) (watches, watch, enabled);
+ (* toggle_function) (connection->watches, watch, enabled);
}
-
- CONNECTION_LOCK (connection);
- connection->watches = watches;
- _dbus_connection_unref_unlocked (connection);
-
return retval;
}
else
DBusTimeoutToggleFunction toggle_function,
dbus_bool_t enabled)
{
- DBusTimeoutList *timeouts;
dbus_bool_t retval;
-
+
HAVE_LOCK_CHECK (connection);
- /* This isn't really safe or reasonable; a better pattern is the "do everything, then
- * drop lock and call out" one; but it has to be propagated up through all callers
+ /* The original purpose of protected_change_timeout() was to hold a
+ * ref on the connection while dropping the connection lock, then
+ * calling out to the app. This was a broken hack that did not
+ * work, since the connection was in a hosed state (no TimeoutList
+ * field) while calling out.
+ *
+ * So for now we'll just keep the lock while calling out. This means
+ * apps are not allowed to call DBusConnection methods inside a
+ * timeout function or they will deadlock.
+ *
+ * The "real fix" is to use the _and_unlock() pattern found
+ * elsewhere in the code, to defer calling out to the app until
+ * we're about to drop locks and return flow of control to the app
+ * anyway.
+ *
+ * See http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
*/
-
- timeouts = connection->timeouts;
- if (timeouts)
- {
- connection->timeouts = NULL;
- _dbus_connection_ref_unlocked (connection);
- CONNECTION_UNLOCK (connection);
+ if (connection->timeouts)
+ {
if (add_function)
- retval = (* add_function) (timeouts, timeout);
+ retval = (* add_function) (connection->timeouts, timeout);
else if (remove_function)
{
retval = TRUE;
- (* remove_function) (timeouts, timeout);
+ (* remove_function) (connection->timeouts, timeout);
}
else
{
retval = TRUE;
- (* toggle_function) (timeouts, timeout, enabled);
+ (* toggle_function) (connection->timeouts, timeout, enabled);
}
-
- CONNECTION_LOCK (connection);
- connection->timeouts = timeouts;
- _dbus_connection_unref_unlocked (connection);
-
return retval;
}
else
/* We will only touch io_path_acquired which is protected by our mutex */
CONNECTION_UNLOCK (connection);
- _dbus_verbose ("%s locking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("locking io_path_mutex\n");
_dbus_mutex_lock (connection->io_path_mutex);
- _dbus_verbose ("%s start connection->io_path_acquired = %d timeout = %d\n",
- _DBUS_FUNCTION_NAME, connection->io_path_acquired, timeout_milliseconds);
+ _dbus_verbose ("start connection->io_path_acquired = %d timeout = %d\n",
+ connection->io_path_acquired, timeout_milliseconds);
we_acquired = FALSE;
{
if (timeout_milliseconds != -1)
{
- _dbus_verbose ("%s waiting %d for IO path to be acquirable\n",
- _DBUS_FUNCTION_NAME, timeout_milliseconds);
+ _dbus_verbose ("waiting %d for IO path to be acquirable\n",
+ timeout_milliseconds);
if (!_dbus_condvar_wait_timeout (connection->io_path_cond,
connection->io_path_mutex,
{
while (connection->io_path_acquired)
{
- _dbus_verbose ("%s waiting for IO path to be acquirable\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("waiting for IO path to be acquirable\n");
_dbus_condvar_wait (connection->io_path_cond,
connection->io_path_mutex);
}
connection->io_path_acquired = TRUE;
}
- _dbus_verbose ("%s end connection->io_path_acquired = %d we_acquired = %d\n",
- _DBUS_FUNCTION_NAME, connection->io_path_acquired, we_acquired);
+ _dbus_verbose ("end connection->io_path_acquired = %d we_acquired = %d\n",
+ connection->io_path_acquired, we_acquired);
- _dbus_verbose ("%s unlocking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("unlocking io_path_mutex\n");
_dbus_mutex_unlock (connection->io_path_mutex);
CONNECTION_LOCK (connection);
{
HAVE_LOCK_CHECK (connection);
- _dbus_verbose ("%s locking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("locking io_path_mutex\n");
_dbus_mutex_lock (connection->io_path_mutex);
_dbus_assert (connection->io_path_acquired);
- _dbus_verbose ("%s start connection->io_path_acquired = %d\n",
- _DBUS_FUNCTION_NAME, connection->io_path_acquired);
+ _dbus_verbose ("start connection->io_path_acquired = %d\n",
+ connection->io_path_acquired);
connection->io_path_acquired = FALSE;
_dbus_condvar_wake_one (connection->io_path_cond);
- _dbus_verbose ("%s unlocking io_path_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("unlocking io_path_mutex\n");
_dbus_mutex_unlock (connection->io_path_mutex);
}
* you specify DBUS_ITERATION_BLOCK; in that case the function
* returns immediately.
*
+ * If pending is not NULL then a check is made if the pending call
+ * is completed after the io path has been required. If the call
+ * has been completed nothing is done. This must be done since
+ * the _dbus_connection_acquire_io_path releases the connection
+ * lock for a while.
+ *
* Called with connection lock held.
*
* @param connection the connection.
+ * @param pending the pending call that should be checked or NULL
* @param flags iteration flags.
* @param timeout_milliseconds maximum blocking time, or -1 for no limit.
*/
void
_dbus_connection_do_iteration_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending,
unsigned int flags,
int timeout_milliseconds)
{
- _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("start\n");
HAVE_LOCK_CHECK (connection);
{
HAVE_LOCK_CHECK (connection);
- _dbus_transport_do_iteration (connection->transport,
- flags, timeout_milliseconds);
+ if ( (pending != NULL) && _dbus_pending_call_get_completed_unlocked(pending))
+ {
+ _dbus_verbose ("pending call completed while acquiring I/O path");
+ }
+ else if ( (pending != NULL) &&
+ _dbus_connection_peek_for_reply_unlocked (connection,
+ _dbus_pending_call_get_reply_serial_unlocked (pending)))
+ {
+ _dbus_verbose ("pending call completed while acquiring I/O path (reply found in queue)");
+ }
+ else
+ {
+ _dbus_transport_do_iteration (connection->transport,
+ flags, timeout_milliseconds);
+ }
+
_dbus_connection_release_io_path (connection);
}
HAVE_LOCK_CHECK (connection);
- _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("end\n");
}
/**
if (connection->io_path_cond == NULL)
goto error;
+ _dbus_mutex_new_at_location (&connection->slot_mutex);
+ if (connection->slot_mutex == NULL)
+ goto error;
+
disconnect_message = dbus_message_new_signal (DBUS_PATH_LOCAL,
DBUS_INTERFACE_LOCAL,
"Disconnected");
_dbus_mutex_free_at_location (&connection->mutex);
_dbus_mutex_free_at_location (&connection->io_path_mutex);
_dbus_mutex_free_at_location (&connection->dispatch_mutex);
+ _dbus_mutex_free_at_location (&connection->slot_mutex);
dbus_free (connection);
}
if (pending_replies)
connection = data;
- _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("start\n");
CONNECTION_LOCK (connection);
- _dbus_connection_acquire_io_path (connection, -1);
+
+ if (!_dbus_connection_acquire_io_path (connection, 1))
+ {
+ /* another thread is handling the message */
+ CONNECTION_UNLOCK (connection);
+ return TRUE;
+ }
+
HAVE_LOCK_CHECK (connection);
retval = _dbus_transport_handle_watch (connection->transport,
watch, condition);
HAVE_LOCK_CHECK (connection);
- _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("middle\n");
status = _dbus_connection_get_dispatch_status_unlocked (connection);
/* this calls out to user code */
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
- _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("end\n");
return retval;
}
dbus_uint32_t *client_serial)
{
dbus_uint32_t serial;
- const char *sig;
preallocated->queue_link->data = message;
_dbus_list_prepend_link (&connection->outgoing_messages,
connection->n_outgoing += 1;
- sig = dbus_message_get_signature (message);
-
- _dbus_verbose ("Message %p (%d %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n",
+ _dbus_verbose ("Message %p (%s %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n",
message,
- dbus_message_get_type (message),
+ dbus_message_type_to_string (dbus_message_get_type (message)),
dbus_message_get_path (message) ?
dbus_message_get_path (message) :
"no path",
dbus_message_get_member (message) ?
dbus_message_get_member (message) :
"no member",
- sig,
+ dbus_message_get_signature (message),
dbus_message_get_destination (message) ?
dbus_message_get_destination (message) :
"null",
* out immediately, and otherwise get them queued up
*/
_dbus_connection_do_iteration_unlocked (connection,
+ NULL,
DBUS_ITERATION_DO_WRITING,
-1);
preallocated,
message, client_serial);
- _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("middle\n");
status = _dbus_connection_get_dispatch_status_unlocked (connection);
/* this calls out to user code */
return message;
}
+/*
+ * Peek the incoming queue to see if we got reply for a specific serial
+ */
+static dbus_bool_t
+_dbus_connection_peek_for_reply_unlocked (DBusConnection *connection,
+ dbus_uint32_t client_serial)
+{
+ DBusList *link;
+ HAVE_LOCK_CHECK (connection);
+
+ link = _dbus_list_get_first_link (&connection->incoming_messages);
+
+ while (link != NULL)
+ {
+ DBusMessage *reply = link->data;
+
+ if (dbus_message_get_reply_serial (reply) == client_serial)
+ {
+ _dbus_verbose ("%s reply to %d found in queue\n", _DBUS_FUNCTION_NAME, client_serial);
+ return TRUE;
+ }
+ link = _dbus_list_get_next_link (&connection->incoming_messages, link);
+ }
+
+ return FALSE;
+}
/* This is slightly strange since we can pop a message here without
* the dispatch lock.
_dbus_pending_call_get_reply_serial_unlocked (pending));
if (reply != NULL)
{
- _dbus_verbose ("%s checked for reply\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("checked for reply\n");
_dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n");
* below
*/
timeout = _dbus_pending_call_get_timeout_unlocked (pending);
+ _dbus_get_current_time (&start_tv_sec, &start_tv_usec);
if (timeout)
{
timeout_milliseconds = dbus_timeout_get_interval (timeout);
- _dbus_get_current_time (&start_tv_sec, &start_tv_usec);
_dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %ld sec %ld usec\n",
timeout_milliseconds,
/* Now we wait... */
/* always block at least once as we know we don't have the reply yet */
_dbus_connection_do_iteration_unlocked (connection,
+ pending,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_BLOCK,
timeout_milliseconds);
recheck_status:
- _dbus_verbose ("%s top of recheck\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("top of recheck\n");
HAVE_LOCK_CHECK (connection);
*/
if (_dbus_pending_call_get_completed_unlocked (pending))
{
- _dbus_verbose ("Pending call completed by dispatch in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("Pending call completed by dispatch\n");
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
dbus_pending_call_unref (pending);
return;
{
/* block again, we don't have the reply buffered yet. */
_dbus_connection_do_iteration_unlocked (connection,
+ pending,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_BLOCK,
timeout_milliseconds - elapsed_milliseconds);
{
/* block again, we don't have the reply buffered yet. */
_dbus_connection_do_iteration_unlocked (connection,
+ NULL,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_BLOCK,
timeout_milliseconds - elapsed_milliseconds);
goto recheck_status;
}
- _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n",
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %d milliseconds and got no reply\n",
elapsed_milliseconds);
_dbus_assert (!_dbus_pending_call_get_completed_unlocked (pending));
/* The connection lock is better than the global
* lock in the atomic increment fallback
+ *
+ * (FIXME but for now we always use the atomic version,
+ * to avoid taking the connection lock, due to
+ * the mess with set_timeout_functions()/set_watch_functions()
+ * calling out to the app without dropping locks)
*/
-#ifdef DBUS_HAVE_ATOMIC_INT
+#if 1
_dbus_atomic_inc (&connection->refcount);
#else
CONNECTION_LOCK (connection);
_dbus_mutex_free_at_location (&connection->io_path_mutex);
_dbus_mutex_free_at_location (&connection->dispatch_mutex);
+ _dbus_mutex_free_at_location (&connection->slot_mutex);
+
_dbus_mutex_free_at_location (&connection->mutex);
dbus_free (connection);
/* The connection lock is better than the global
* lock in the atomic increment fallback
+ *
+ * (FIXME but for now we always use the atomic version,
+ * to avoid taking the connection lock, due to
+ * the mess with set_timeout_functions()/set_watch_functions()
+ * calling out to the app without dropping locks)
*/
-#ifdef DBUS_HAVE_ATOMIC_INT
+#if 1
last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
#else
CONNECTION_LOCK (connection);
{
_dbus_return_val_if_fail (connection != NULL, FALSE);
- if (!_dbus_type_is_valid(type))
+ if (!dbus_type_is_valid (type))
return FALSE;
if (type != DBUS_TYPE_UNIX_FD)
_dbus_pending_call_get_timeout_unlocked (pending));
_dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
- _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("middle\n");
status = _dbus_connection_get_dispatch_status_unlocked (connection);
/* Unlocks, and calls out to user code */
while (connection->n_outgoing > 0 &&
_dbus_connection_get_is_connected_unlocked (connection))
{
- _dbus_verbose ("doing iteration in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("doing iteration in\n");
HAVE_LOCK_CHECK (connection);
_dbus_connection_do_iteration_unlocked (connection,
+ NULL,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_DO_WRITING |
DBUS_ITERATION_BLOCK,
}
HAVE_LOCK_CHECK (connection);
- _dbus_verbose ("%s middle\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("middle\n");
status = _dbus_connection_get_dispatch_status_unlocked (connection);
HAVE_LOCK_CHECK (connection);
/* Unlocks and calls out to user code */
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
- _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("end\n");
}
/**
if (dispatch && dstatus == DBUS_DISPATCH_DATA_REMAINS)
{
- _dbus_verbose ("doing dispatch in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("doing dispatch\n");
dbus_connection_dispatch (connection);
CONNECTION_LOCK (connection);
}
else if (dstatus == DBUS_DISPATCH_NEED_MEMORY)
{
- _dbus_verbose ("pausing for memory in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("pausing for memory\n");
_dbus_memory_pause_based_on_timeout (timeout_milliseconds);
CONNECTION_LOCK (connection);
}
CONNECTION_LOCK (connection);
if (_dbus_connection_get_is_connected_unlocked (connection))
{
- _dbus_verbose ("doing iteration in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("doing iteration\n");
_dbus_connection_do_iteration_unlocked (connection,
+ NULL,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_DO_WRITING |
DBUS_ITERATION_BLOCK,
_dbus_return_val_if_fail (connection != NULL, NULL);
- _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("start\n");
/* this is called for the side effect that it queues
* up any messages from the transport
link = _dbus_list_pop_first_link (&connection->incoming_messages);
connection->n_incoming -= 1;
- _dbus_verbose ("Message %p (%d %s %s %s '%s') removed from incoming queue %p, %d incoming\n",
+ _dbus_verbose ("Message %p (%s %s %s %s '%s') removed from incoming queue %p, %d incoming\n",
link->data,
- dbus_message_get_type (link->data),
+ dbus_message_type_to_string (dbus_message_get_type (link->data)),
dbus_message_get_path (link->data) ?
dbus_message_get_path (link->data) :
"no path",
message_link);
connection->n_incoming += 1;
- _dbus_verbose ("Message %p (%d %s %s '%s') put back into queue %p, %d incoming\n",
+ _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
message_link->data,
- dbus_message_get_type (message_link->data),
+ dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
dbus_message_get_interface (message_link->data) ?
dbus_message_get_interface (message_link->data) :
"no interface",
DBusMessage *message;
DBusDispatchStatus status;
- _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("start\n");
/* this is called for the side effect that it queues
* up any messages from the transport
_dbus_connection_ref_unlocked (connection);
CONNECTION_UNLOCK (connection);
- _dbus_verbose ("%s locking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("locking dispatch_mutex\n");
_dbus_mutex_lock (connection->dispatch_mutex);
while (connection->dispatch_acquired)
{
- _dbus_verbose ("%s waiting for dispatch to be acquirable\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("waiting for dispatch to be acquirable\n");
_dbus_condvar_wait (connection->dispatch_cond,
connection->dispatch_mutex);
}
connection->dispatch_acquired = TRUE;
- _dbus_verbose ("%s unlocking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("unlocking dispatch_mutex\n");
_dbus_mutex_unlock (connection->dispatch_mutex);
CONNECTION_LOCK (connection);
{
HAVE_LOCK_CHECK (connection);
- _dbus_verbose ("%s locking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("locking dispatch_mutex\n");
_dbus_mutex_lock (connection->dispatch_mutex);
_dbus_assert (connection->dispatch_acquired);
connection->dispatch_acquired = FALSE;
_dbus_condvar_wake_one (connection->dispatch_cond);
- _dbus_verbose ("%s unlocking dispatch_mutex\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("unlocking dispatch_mutex\n");
_dbus_mutex_unlock (connection->dispatch_mutex);
}
if (connection->disconnect_message_link != NULL)
{
- _dbus_verbose ("Sending disconnect message from %s\n",
- _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("Sending disconnect message\n");
/* If we have pending calls, queue their timeouts - we want the Disconnected
* to be the last message, after these timeouts.
_dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
- _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("start\n");
CONNECTION_LOCK (connection);
_dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
- _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("\n");
CONNECTION_LOCK (connection);
status = _dbus_connection_get_dispatch_status_unlocked (connection);
message = message_link->data;
- _dbus_verbose (" dispatching message %p (%d %s %s '%s')\n",
+ _dbus_verbose (" dispatching message %p (%s %s %s '%s')\n",
message,
- dbus_message_get_type (message),
+ dbus_message_type_to_string (dbus_message_get_type (message)),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
{
- _dbus_verbose ("No memory in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("No memory\n");
goto out;
}
else if (result == DBUS_HANDLER_RESULT_HANDLED)
/* We're still protected from dispatch() reentrancy here
* since we acquired the dispatcher
*/
- _dbus_verbose (" running object path dispatch on message %p (%d %s %s '%s')\n",
+ _dbus_verbose (" running object path dispatch on message %p (%s %s %s '%s')\n",
message,
- dbus_message_get_type (message),
+ dbus_message_type_to_string (dbus_message_get_type (message)),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
result = DBUS_HANDLER_RESULT_HANDLED;
}
- _dbus_verbose (" done dispatching %p (%d %s %s '%s') on connection %p\n", message,
- dbus_message_get_type (message),
+ _dbus_verbose (" done dispatching %p (%s %s %s '%s') on connection %p\n", message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
out:
if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
{
- _dbus_verbose ("out of memory in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("out of memory\n");
/* Put message back, and we'll start over.
* Yes this means handlers must be idempotent if they
}
else
{
- _dbus_verbose (" ... done dispatching in %s\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose (" ... done dispatching\n");
_dbus_list_free_link (message_link);
dbus_message_unref (message); /* don't want the message to count in max message limits
_dbus_connection_release_dispatch (connection);
HAVE_LOCK_CHECK (connection);
- _dbus_verbose ("%s before final status update\n", _DBUS_FUNCTION_NAME);
+ _dbus_verbose ("before final status update\n");
status = _dbus_connection_get_dispatch_status_unlocked (connection);
/* unlocks and calls user code */
* should be that dbus_connection_set_watch_functions() has no effect,
* but the add_function and remove_function may have been called.
*
- * @todo We need to drop the lock when we call the
- * add/remove/toggled functions which can be a side effect
- * of setting the watch functions.
+ * @note The thread lock on DBusConnection is held while
+ * watch functions are invoked, so inside these functions you
+ * may not invoke any methods on DBusConnection or it will deadlock.
+ * See the comments in the code or http://lists.freedesktop.org/archives/dbus/2007-July/tread.html#8144
+ * if you encounter this issue and want to attempt writing a patch.
*
* @param connection the connection.
* @param add_function function to begin monitoring a new descriptor.
DBusFreeFunction free_data_function)
{
dbus_bool_t retval;
- DBusWatchList *watches;
_dbus_return_val_if_fail (connection != NULL, FALSE);
CONNECTION_LOCK (connection);
-#ifndef DBUS_DISABLE_CHECKS
- if (connection->watches == NULL)
- {
- _dbus_warn_check_failed ("Re-entrant call to %s is not allowed\n",
- _DBUS_FUNCTION_NAME);
- return FALSE;
- }
-#endif
-
- /* ref connection for slightly better reentrancy */
- _dbus_connection_ref_unlocked (connection);
-
- /* This can call back into user code, and we need to drop the
- * connection lock when it does. This is kind of a lame
- * way to do it.
- */
- watches = connection->watches;
- connection->watches = NULL;
- CONNECTION_UNLOCK (connection);
-
- retval = _dbus_watch_list_set_functions (watches,
+ retval = _dbus_watch_list_set_functions (connection->watches,
add_function, remove_function,
toggled_function,
data, free_data_function);
- CONNECTION_LOCK (connection);
- connection->watches = watches;
-
+
CONNECTION_UNLOCK (connection);
- /* drop our paranoid refcount */
- dbus_connection_unref (connection);
-
+
return retval;
}
* given remove_function. The timer interval may change whenever the
* timeout is added, removed, or toggled.
*
+ * @note The thread lock on DBusConnection is held while
+ * timeout functions are invoked, so inside these functions you
+ * may not invoke any methods on DBusConnection or it will deadlock.
+ * See the comments in the code or http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
+ * if you encounter this issue and want to attempt writing a patch.
+ *
* @param connection the connection.
* @param add_function function to add a timeout.
* @param remove_function function to remove a timeout.
DBusFreeFunction free_data_function)
{
dbus_bool_t retval;
- DBusTimeoutList *timeouts;
_dbus_return_val_if_fail (connection != NULL, FALSE);
CONNECTION_LOCK (connection);
-#ifndef DBUS_DISABLE_CHECKS
- if (connection->timeouts == NULL)
- {
- _dbus_warn_check_failed ("Re-entrant call to %s is not allowed\n",
- _DBUS_FUNCTION_NAME);
- return FALSE;
- }
-#endif
-
- /* ref connection for slightly better reentrancy */
- _dbus_connection_ref_unlocked (connection);
-
- timeouts = connection->timeouts;
- connection->timeouts = NULL;
- CONNECTION_UNLOCK (connection);
-
- retval = _dbus_timeout_list_set_functions (timeouts,
+ retval = _dbus_timeout_list_set_functions (connection->timeouts,
add_function, remove_function,
toggled_function,
data, free_data_function);
- CONNECTION_LOCK (connection);
- connection->timeouts = timeouts;
-
+
CONNECTION_UNLOCK (connection);
- /* drop our paranoid refcount */
- dbus_connection_unref (connection);
return retval;
}
* the connection is finalized. The slot number
* must have been allocated with dbus_connection_allocate_data_slot().
*
+ * @note This function does not take the
+ * main thread lock on DBusConnection, which allows it to be
+ * used from inside watch and timeout functions. (See the
+ * note in docs for dbus_connection_set_watch_functions().)
+ * A side effect of this is that you need to know there's
+ * a reference held on the connection while invoking
+ * dbus_connection_set_data(), or the connection could be
+ * finalized during dbus_connection_set_data().
+ *
* @param connection the connection
* @param slot the slot number
* @param data the data to store
_dbus_return_val_if_fail (connection != NULL, FALSE);
_dbus_return_val_if_fail (slot >= 0, FALSE);
- CONNECTION_LOCK (connection);
+ SLOTS_LOCK (connection);
retval = _dbus_data_slot_list_set (&slot_allocator,
&connection->slot_list,
slot, data, free_data_func,
&old_free_func, &old_data);
- CONNECTION_UNLOCK (connection);
+ SLOTS_UNLOCK (connection);
if (retval)
{
* Retrieves data previously set with dbus_connection_set_data().
* The slot must still be allocated (must not have been freed).
*
+ * @note This function does not take the
+ * main thread lock on DBusConnection, which allows it to be
+ * used from inside watch and timeout functions. (See the
+ * note in docs for dbus_connection_set_watch_functions().)
+ * A side effect of this is that you need to know there's
+ * a reference held on the connection while invoking
+ * dbus_connection_get_data(), or the connection could be
+ * finalized during dbus_connection_get_data().
+ *
* @param connection the connection
* @param slot the slot to get data from
* @returns the data, or #NULL if not found
_dbus_return_val_if_fail (connection != NULL, NULL);
- CONNECTION_LOCK (connection);
+ SLOTS_LOCK (connection);
res = _dbus_data_slot_list_get (&slot_allocator,
&connection->slot_list,
slot);
- CONNECTION_UNLOCK (connection);
+ SLOTS_UNLOCK (connection);
return res;
}