#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"
_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_assert (filter->refcount.value > 0);
+#ifdef DBUS_DISABLE_ASSERT
_dbus_atomic_inc (&filter->refcount);
+#else
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_inc (&filter->refcount);
+ _dbus_assert (old_value > 0);
+#endif
return filter;
}
static void
_dbus_message_filter_unref (DBusMessageFilter *filter)
{
- _dbus_assert (filter->refcount.value > 0);
+ dbus_int32_t old_value;
- if (_dbus_atomic_dec (&filter->refcount) == 1)
+ old_value = _dbus_atomic_dec (&filter->refcount);
+ _dbus_assert (old_value > 0);
+
+ if (old_value == 1)
{
if (filter->free_user_data_function)
(* filter->free_user_data_function) (filter->user_data);
* @param message the message that was sent.
*/
void
-_dbus_connection_message_sent (DBusConnection *connection,
- DBusMessage *message)
+_dbus_connection_message_sent_unlocked (DBusConnection *connection,
+ DBusMessage *message)
{
DBusList *link;
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
* 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)
{
{
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);
}
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");
if (_dbus_modify_sigpipe)
_dbus_disable_sigpipe ();
-
- connection->refcount.value = 1;
+
+ /* initialized to 0: use atomic op to avoid mixing atomic and non-atomic */
+ _dbus_atomic_inc (&connection->refcount);
connection->transport = transport;
connection->watches = watch_list;
connection->timeouts = timeout_list;
_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)
_dbus_assert (connection->generation == _dbus_current_generation);
HAVE_LOCK_CHECK (connection);
-
-#ifdef DBUS_HAVE_ATOMIC_INT
+
_dbus_atomic_inc (&connection->refcount);
-#else
- _dbus_assert (connection->refcount.value > 0);
- connection->refcount.value += 1;
-#endif
return connection;
}
_dbus_assert (connection != NULL);
- /* The connection lock is better than the global
- * lock in the atomic increment fallback
- */
-
-#ifdef DBUS_HAVE_ATOMIC_INT
last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
-#else
- _dbus_assert (connection->refcount.value > 0);
- connection->refcount.value -= 1;
- last_unref = (connection->refcount.value == 0);
-#if 0
- printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value);
-#endif
-#endif
-
if (last_unref)
_dbus_connection_last_unref (connection);
}
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 (%s %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n",
message,
dbus_message_type_to_string (dbus_message_get_type (message)),
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);
void
_dbus_connection_close_if_only_one_ref (DBusConnection *connection)
{
+ dbus_int32_t refcount;
+
CONNECTION_LOCK (connection);
-
- _dbus_assert (connection->refcount.value > 0);
- if (connection->refcount.value == 1)
+ refcount = _dbus_atomic_get (&connection->refcount);
+ /* The caller should have at least one ref */
+ _dbus_assert (refcount >= 1);
+
+ if (refcount == 1)
_dbus_connection_close_possibly_shared_and_unlock (connection);
else
CONNECTION_UNLOCK (connection);
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.
* 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);
{
/* 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);
{
_dbus_return_val_if_fail (connection != NULL, NULL);
_dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL);
-
- /* The connection lock is better than the global
- * lock in the atomic increment fallback
- */
-
-#ifdef DBUS_HAVE_ATOMIC_INT
- _dbus_atomic_inc (&connection->refcount);
-#else
- CONNECTION_LOCK (connection);
- _dbus_assert (connection->refcount.value > 0);
- connection->refcount.value += 1;
- CONNECTION_UNLOCK (connection);
-#endif
+ _dbus_atomic_inc (&connection->refcount);
return connection;
}
DBusList *link;
_dbus_verbose ("Finalizing connection %p\n", connection);
-
- _dbus_assert (connection->refcount.value == 0);
-
+
+ _dbus_assert (_dbus_atomic_get (&connection->refcount) == 0);
+
/* You have to disconnect the connection before unref:ing it. Otherwise
* you won't get the disconnected message.
*/
_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);
_dbus_return_if_fail (connection != NULL);
_dbus_return_if_fail (connection->generation == _dbus_current_generation);
-
- /* The connection lock is better than the global
- * lock in the atomic increment fallback
- */
-
-#ifdef DBUS_HAVE_ATOMIC_INT
- last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
-#else
- CONNECTION_LOCK (connection);
-
- _dbus_assert (connection->refcount.value > 0);
- connection->refcount.value -= 1;
- last_unref = (connection->refcount.value == 0);
+ last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
-#if 0
- printf ("unref() connection %p count = %d\n", connection, connection->refcount.value);
-#endif
-
- CONNECTION_UNLOCK (connection);
-#endif
-
if (last_unref)
{
#ifndef DBUS_DISABLE_CHECKS
* This function can be used to do runtime checking for types that
* might be unknown to the specific D-Bus client implementation
* version, i.e. it will return FALSE for all types this
- * implementation does not know.
+ * implementation does not know, including invalid or reserved types.
*
* @param connection the connection
* @param type the type to check
{
_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)
DBusPendingCall *pending = data;
connection = _dbus_pending_call_get_connection_and_lock (pending);
+ _dbus_connection_ref_unlocked (connection);
_dbus_pending_call_queue_timeout_error_unlocked (pending,
connection);
/* Unlocks, and calls out to user code */
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ dbus_connection_unref (connection);
return TRUE;
}
*
* If -1 is passed for the timeout, a sane default timeout is used. -1
* is typically the best value for the timeout for this reason, unless
- * you want a very short or very long timeout. If INT_MAX is passed for
- * the timeout, no timeout will be set and the call will block forever.
+ * you want a very short or very long timeout. If #DBUS_TIMEOUT_INFINITE is
+ * passed for the timeout, no timeout will be set and the call will block
+ * forever.
*
* @warning if the connection is disconnected or you try to send Unix
* file descriptors on a connection that does not support them, the
* object, or #NULL if connection is disconnected or when you try to
* send Unix file descriptors on a connection that does not support
* them.
- * @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout
+ * @param timeout_milliseconds timeout in milliseconds, -1 (or
+ * #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
+ * timeout
* @returns #FALSE if no memory, #TRUE otherwise.
*
*/
*
* @param connection the connection
* @param message the message to send
- * @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout.
+ * @param timeout_milliseconds timeout in milliseconds, -1 (or
+ * #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
+ * timeout
* @param error return location for error message
* @returns the message that is the reply or #NULL with an error code if the
* function fails.
_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,
{
_dbus_verbose ("doing iteration\n");
_dbus_connection_do_iteration_unlocked (connection,
+ NULL,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_DO_WRITING |
DBUS_ITERATION_BLOCK,
while ((link = _dbus_list_get_last_link (&connection->outgoing_messages)))
{
- _dbus_connection_message_sent (connection, link->data);
+ _dbus_connection_message_sent_unlocked (connection, link->data);
}
}
}
DBusPendingCall *pending;
dbus_int32_t reply_serial;
DBusDispatchStatus status;
+ dbus_bool_t found_object;
_dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
/* unlocks and calls user code */
_dbus_connection_update_dispatch_status_and_unlock (connection,
DBUS_DISPATCH_NEED_MEMORY);
-
- if (pending)
- dbus_pending_call_unref (pending);
dbus_connection_unref (connection);
return DBUS_DISPATCH_NEED_MEMORY;
HAVE_LOCK_CHECK (connection);
result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
- message);
+ message,
+ &found_object);
CONNECTION_LOCK (connection);
}
reply = dbus_message_new_error (message,
- DBUS_ERROR_UNKNOWN_METHOD,
+ found_object ? DBUS_ERROR_UNKNOWN_METHOD : DBUS_ERROR_UNKNOWN_OBJECT,
_dbus_string_get_const_data (&str));
_dbus_string_free (&str);
* 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 is not allowed\n");
- 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 is not allowed\n");
- 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;
}
if (filter == NULL)
return FALSE;
- filter->refcount.value = 1;
-
+ _dbus_atomic_inc (&filter->refcount);
+
CONNECTION_LOCK (connection);
if (!_dbus_list_append (&connection->filter_list,
}
/**
- * Registers a handler for a given path in the object hierarchy.
- * The given vtable handles messages sent to exactly the given path.
+ * Registers a handler for a given path or subsection in the object
+ * hierarchy. The given vtable handles messages sent to exactly the
+ * given path or also for paths bellow that, depending on fallback
+ * parameter.
*
* @param connection the connection
+ * @param fallback whether to handle messages also for "subdirectory"
* @param path a '/' delimited string of path elements
* @param vtable the virtual table
* @param user_data data to pass to functions in the vtable
* @param error address where an error can be returned
* @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
- * #DBUS_ERROR_ADDRESS_IN_USE) is reported
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
*/
-dbus_bool_t
-dbus_connection_try_register_object_path (DBusConnection *connection,
- const char *path,
- const DBusObjectPathVTable *vtable,
- void *user_data,
- DBusError *error)
+static dbus_bool_t
+_dbus_connection_register_object_path (DBusConnection *connection,
+ dbus_bool_t fallback,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
{
char **decomposed_path;
dbus_bool_t retval;
-
- _dbus_return_val_if_fail (connection != NULL, FALSE);
- _dbus_return_val_if_fail (path != NULL, FALSE);
- _dbus_return_val_if_fail (path[0] == '/', FALSE);
- _dbus_return_val_if_fail (vtable != NULL, FALSE);
if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
return FALSE;
CONNECTION_LOCK (connection);
retval = _dbus_object_tree_register (connection->objects,
- FALSE,
+ fallback,
(const char **) decomposed_path, vtable,
user_data, error);
* Registers a handler for a given path in the object hierarchy.
* The given vtable handles messages sent to exactly the given path.
*
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+dbus_bool_t
+dbus_connection_try_register_object_path (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ return _dbus_connection_register_object_path (connection, FALSE, path, vtable, user_data, error);
+}
+
+/**
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
+ *
* It is a bug to call this function for object paths which already
* have a handler. Use dbus_connection_try_register_object_path() if this
* might be the case.
* @param path a '/' delimited string of path elements
* @param vtable the virtual table
* @param user_data data to pass to functions in the vtable
- * @returns #FALSE if not enough memory
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) ocurred
*/
dbus_bool_t
dbus_connection_register_object_path (DBusConnection *connection,
const DBusObjectPathVTable *vtable,
void *user_data)
{
- char **decomposed_path;
dbus_bool_t retval;
DBusError error = DBUS_ERROR_INIT;
_dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
- if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
- return FALSE;
-
- CONNECTION_LOCK (connection);
-
- retval = _dbus_object_tree_register (connection->objects,
- FALSE,
- (const char **) decomposed_path, vtable,
- user_data, &error);
-
- CONNECTION_UNLOCK (connection);
-
- dbus_free_string_array (decomposed_path);
+ retval = _dbus_connection_register_object_path (connection, FALSE, path, vtable, user_data, &error);
- if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE))
+ if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
{
_dbus_warn ("%s\n", error.message);
dbus_error_free (&error);
* @param user_data data to pass to functions in the vtable
* @param error address where an error can be returned
* @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
- * #DBUS_ERROR_ADDRESS_IN_USE) is reported
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
*/
dbus_bool_t
dbus_connection_try_register_fallback (DBusConnection *connection,
void *user_data,
DBusError *error)
{
- char **decomposed_path;
- dbus_bool_t retval;
-
_dbus_return_val_if_fail (connection != NULL, FALSE);
_dbus_return_val_if_fail (path != NULL, FALSE);
_dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
- if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
- return FALSE;
-
- CONNECTION_LOCK (connection);
-
- retval = _dbus_object_tree_register (connection->objects,
- TRUE,
- (const char **) decomposed_path, vtable,
- user_data, error);
-
- CONNECTION_UNLOCK (connection);
-
- dbus_free_string_array (decomposed_path);
-
- return retval;
+ return _dbus_connection_register_object_path (connection, TRUE, path, vtable, user_data, error);
}
/**
* @param path a '/' delimited string of path elements
* @param vtable the virtual table
* @param user_data data to pass to functions in the vtable
- * @returns #FALSE if not enough memory
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) occured
*/
dbus_bool_t
dbus_connection_register_fallback (DBusConnection *connection,
const DBusObjectPathVTable *vtable,
void *user_data)
{
- char **decomposed_path;
dbus_bool_t retval;
DBusError error = DBUS_ERROR_INIT;
_dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
- if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
- return FALSE;
-
- CONNECTION_LOCK (connection);
-
- retval = _dbus_object_tree_register (connection->objects,
- TRUE,
- (const char **) decomposed_path, vtable,
- user_data, &error);
-
- CONNECTION_UNLOCK (connection);
-
- dbus_free_string_array (decomposed_path);
+ retval = _dbus_connection_register_object_path (connection, TRUE, path, vtable, user_data, &error);
- if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE))
+ if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
{
_dbus_warn ("%s\n", error.message);
dbus_error_free (&error);
* 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;
}
return res;
}
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_connection_get_stats (DBusConnection *connection,
+ dbus_uint32_t *in_messages,
+ dbus_uint32_t *in_bytes,
+ dbus_uint32_t *in_fds,
+ dbus_uint32_t *in_peak_bytes,
+ dbus_uint32_t *in_peak_fds,
+ dbus_uint32_t *out_messages,
+ dbus_uint32_t *out_bytes,
+ dbus_uint32_t *out_fds,
+ dbus_uint32_t *out_peak_bytes,
+ dbus_uint32_t *out_peak_fds,
+ dbus_uint32_t *link_cache_size)
+{
+ CONNECTION_LOCK (connection);
+
+ if (in_messages != NULL)
+ *in_messages = connection->n_incoming;
+
+ _dbus_transport_get_stats (connection->transport,
+ in_bytes, in_fds, in_peak_bytes, in_peak_fds);
+
+ if (out_messages != NULL)
+ *out_messages = connection->n_outgoing;
+
+ if (out_bytes != NULL)
+ *out_bytes = _dbus_counter_get_size_value (connection->outgoing_counter);
+
+ if (out_fds != NULL)
+ *out_fds = _dbus_counter_get_unix_fd_value (connection->outgoing_counter);
+
+ if (out_peak_bytes != NULL)
+ *out_peak_bytes = _dbus_counter_get_peak_size_value (connection->outgoing_counter);
+
+ if (out_peak_fds != NULL)
+ *out_peak_fds = _dbus_counter_get_peak_unix_fd_value (connection->outgoing_counter);
+
+ if (link_cache_size != NULL)
+ {
+ *link_cache_size = _dbus_list_get_length (&connection->link_cache) * sizeof (DBusList);
+ }
+
+ CONNECTION_UNLOCK (connection);
+}
+#endif /* DBUS_ENABLE_STATS */
+
/**
* Gets the approximate number of uni fds of all messages in the
* outgoing message queue.