summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2013-09-26 13:29:49 +0200
committerAlexander Larsson <alexl@redhat.com>2013-11-13 13:06:33 +0100
commitfd0feb2d8a6a74854232c9344402c871da8e5bb5 (patch)
tree46b4d8b22b29f4e661991fc3763f33478c8a17dd
parent1b51c45d19a62148d7cdaffbc4a70b3748598b45 (diff)
downloadgvfs-fd0feb2d8a6a74854232c9344402c871da8e5bb5.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.c36
1 files changed, 26 insertions, 10 deletions
diff --git a/daemon/gvfsdaemon.c b/daemon/gvfsdaemon.c
index 7db8ad2c..7894a7bc 100644
--- a/daemon/gvfsdaemon.c
+++ b/daemon/gvfsdaemon.c
@@ -608,19 +608,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 */