diff options
author | Maciej Mrozowski <reavertm@gentoo.org> | 2010-08-05 19:17:22 +0000 |
---|---|---|
committer | Maciej Mrozowski <reavertm@gentoo.org> | 2010-08-05 19:17:22 +0000 |
commit | 1ed0a63891508090ced1b40c21aaa0dcad31e8e4 (patch) | |
tree | 7f599fa458e1fdd87fe6e820655bcfd1e536e215 /sys-apps/dbus/files | |
parent | Mask www-client/chromium dev channel release. (diff) | |
download | gentoo-2-1ed0a63891508090ced1b40c21aaa0dcad31e8e4.tar.gz gentoo-2-1ed0a63891508090ced1b40c21aaa0dcad31e8e4.tar.bz2 gentoo-2-1ed0a63891508090ced1b40c21aaa0dcad31e8e4.zip |
Commited backport from master to 1.2.24 (bug https://bugs.freedesktop.org/show_bug.cgi?id=17754) - patch to fix thread safety in protected_change_timeout for further review.
(Portage version: 2.2_rc67/cvs/Linux x86_64)
Diffstat (limited to 'sys-apps/dbus/files')
-rw-r--r-- | sys-apps/dbus/files/dbus-1.2.24-thread-safety.patch | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/sys-apps/dbus/files/dbus-1.2.24-thread-safety.patch b/sys-apps/dbus/files/dbus-1.2.24-thread-safety.patch new file mode 100644 index 000000000000..79ffd51e2dd0 --- /dev/null +++ b/sys-apps/dbus/files/dbus-1.2.24-thread-safety.patch @@ -0,0 +1,393 @@ +https://bugs.freedesktop.org/show_bug.cgi?id=17754 + +backport from 6ff1d079316cb730a54b4e0e95bd3e6e31f439de (master) + +diff -ru ../dbus-1.2.24/dbus/dbus-connection.c ./dbus/dbus-connection.c +--- ../dbus-1.2.24/dbus/dbus-connection.c 2010-03-23 20:01:32.000000000 +0100 ++++ ./dbus/dbus-connection.c 2010-08-04 07:20:00.310381625 +0200 +@@ -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 @@ + + 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. */ +@@ -647,39 +656,42 @@ + 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 +@@ -768,39 +780,42 @@ + 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 +@@ -1239,6 +1254,10 @@ + 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"); +@@ -1315,6 +1334,7 @@ + _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) +@@ -2558,9 +2578,14 @@ + + /* 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); +@@ -2672,6 +2697,8 @@ + _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); +@@ -2706,9 +2733,14 @@ + + /* 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); +@@ -4652,9 +4684,11 @@ + * 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. +@@ -4673,42 +4707,17 @@ + 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; + } +@@ -4738,6 +4747,12 @@ + * 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. +@@ -4755,38 +4770,17 @@ + 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; + } +@@ -5749,6 +5743,15 @@ + * 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 +@@ -5768,14 +5771,14 @@ + _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) + { +@@ -5791,6 +5794,15 @@ + * 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 +@@ -5803,13 +5815,13 @@ + + _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; + } |