mirror of
https://gitdl.cn/https://github.com/chakralinux/core.git
synced 2025-01-25 02:52:16 +08:00
393 lines
14 KiB
Diff
393 lines
14 KiB
Diff
|
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
|
||
|
index b3cfa3d..e6d261f 100644
|
||
|
--- a/dbus/dbus-connection.c
|
||
|
+++ b/dbus/dbus-connection.c
|
||
|
@@ -73,6 +73,14 @@
|
||
|
_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" : \
|
||
|
@@ -257,6 +265,7 @@ struct DBusConnection
|
||
|
|
||
|
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. */
|
||
|
@@ -649,39 +658,42 @@ protected_change_watch (DBusConnection *connection,
|
||
|
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
|
||
|
@@ -770,39 +782,42 @@ protected_change_timeout (DBusConnection *connection,
|
||
|
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
|
||
|
@@ -1263,6 +1278,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
|
||
|
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");
|
||
|
@@ -1339,6 +1358,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
|
||
|
_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)
|
||
|
@@ -2612,9 +2632,14 @@ dbus_connection_ref (DBusConnection *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
|
||
|
_dbus_atomic_inc (&connection->refcount);
|
||
|
#else
|
||
|
CONNECTION_LOCK (connection);
|
||
|
@@ -2726,6 +2751,8 @@ _dbus_connection_last_unref (DBusConnection *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);
|
||
|
@@ -2760,9 +2787,14 @@ dbus_connection_unref (DBusConnection *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);
|
||
|
@@ -4708,9 +4740,11 @@ dbus_connection_dispatch (DBusConnection *connection)
|
||
|
* 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.
|
||
|
@@ -4729,43 +4763,18 @@ dbus_connection_set_watch_functions (DBusConnection *connection,
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
@@ -4794,6 +4803,12 @@ dbus_connection_set_watch_functions (DBusConnection *connection,
|
||
|
* 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.
|
||
|
@@ -4811,38 +4826,17 @@ dbus_connection_set_timeout_functions (DBusConnection *connection,
|
||
|
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;
|
||
|
}
|
||
|
@@ -5805,6 +5799,15 @@ dbus_connection_free_data_slot (dbus_int32_t *slot_p)
|
||
|
* 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
|
||
|
@@ -5824,14 +5827,14 @@ dbus_connection_set_data (DBusConnection *connection,
|
||
|
_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)
|
||
|
{
|
||
|
@@ -5847,6 +5850,15 @@ dbus_connection_set_data (DBusConnection *connection,
|
||
|
* 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
|
||
|
@@ -5859,13 +5871,13 @@ dbus_connection_get_data (DBusConnection *connection,
|
||
|
|
||
|
_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;
|
||
|
}
|