diff options
author | Alexander Larsson <alexl@redhat.com> | 2013-09-26 13:29:49 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2013-09-26 13:34:03 +0200 |
commit | 3448fa01d514e31d0b0c1071f7738b3c8101c8ab (patch) | |
tree | 6e4ac4ce0a6fa88c2808b5c0a4637919463a0d3e | |
parent | 8d08c558cda63e864ffd1c960435ea5332c884bd (diff) | |
download | gvfs-3448fa01d514e31d0b0c1071f7738b3c8101c8ab.tar.gz |
GVfsDaemon: Don't deadlock on cliend disconnect
There was a deadlock that could happen on client disconnect in
peer_connection_closed(). If the cancelled job immediately called back into
job_finished_callback() we would deadlock on daemon->mutex.
So, make sure we cancel jobs without holding the lock, just like
handle_cancel() does.
https://bugzilla.gnome.org/show_bug.cgi?id=708816
-rw-r--r-- | daemon/gvfsdaemon.c | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/daemon/gvfsdaemon.c b/daemon/gvfsdaemon.c index 6cb3341a..9a9cb008 100644 --- a/daemon/gvfsdaemon.c +++ b/daemon/gvfsdaemon.c @@ -624,19 +624,35 @@ peer_connection_closed (GDBusConnection *connection, GVfsDaemon *daemon = G_VFS_DAEMON (user_data); GList *l; GVfsDBusDaemon *daemon_skeleton; + GVfsJob *job_to_cancel; - g_mutex_lock (&daemon->lock); - for (l = daemon->jobs; l != NULL; l = l->next) + do { - GVfsJob *job = G_VFS_JOB (l->data); - - if (G_VFS_IS_JOB_DBUS (job) && - G_VFS_JOB_DBUS (job)->invocation && - g_dbus_method_invocation_get_connection (G_VFS_JOB_DBUS (job)->invocation) == connection) - g_vfs_job_cancel (job); - } - g_mutex_unlock (&daemon->lock); + job_to_cancel = NULL; + + g_mutex_lock (&daemon->lock); + for (l = daemon->jobs; l != NULL; l = l->next) + { + GVfsJob *job = G_VFS_JOB (l->data); + + if (G_VFS_IS_JOB_DBUS (job) && + !g_vfs_job_is_cancelled (job) && + G_VFS_JOB_DBUS (job)->invocation && + g_dbus_method_invocation_get_connection (G_VFS_JOB_DBUS (job)->invocation) == connection) + { + job_to_cancel = g_object_ref (job); + break; + } + } + g_mutex_unlock (&daemon->lock); + if (job_to_cancel) + { + g_vfs_job_cancel (job_to_cancel); + g_object_unref (job_to_cancel); + } + } + while (job_to_cancel != NULL); daemon_skeleton = g_object_get_data (G_OBJECT (connection), "daemon_skeleton"); /* daemon_skeleton should be always valid in this case */ |