[evolution] Bug #664634 - Deadlock when processing completed tasks filter



commit 4169ce41cc34d992ebd63b1bcc8f604e69aea89a
Author: Milan Crha <mcrha redhat com>
Date:   Thu Nov 24 16:35:59 2011 +0100

    Bug #664634 - Deadlock when processing completed tasks filter

 calendar/gui/e-cal-model.c                   |    2 +
 calendar/gui/e-task-table.c                  |  244 ++++++++++++++-----------
 modules/calendar/e-task-shell-view-private.c |   30 +++-
 modules/calendar/e-task-shell-view-private.h |    1 +
 4 files changed, 165 insertions(+), 112 deletions(-)
---
diff --git a/calendar/gui/e-cal-model.c b/calendar/gui/e-cal-model.c
index 4428d31..fb98987 100644
--- a/calendar/gui/e-cal-model.c
+++ b/calendar/gui/e-cal-model.c
@@ -3752,7 +3752,9 @@ e_cal_model_generate_instances_sync (ECalModel *model,
 GPtrArray *
 e_cal_model_get_object_array (ECalModel *model)
 {
+	g_return_val_if_fail (model != NULL, NULL);
 	g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
+	g_return_val_if_fail (model->priv != NULL, NULL);
 
 	return model->priv->objects;
 }
diff --git a/calendar/gui/e-task-table.c b/calendar/gui/e-task-table.c
index 0a38889..e052e82 100644
--- a/calendar/gui/e-task-table.c
+++ b/calendar/gui/e-task-table.c
@@ -66,6 +66,7 @@
 struct _ETaskTablePrivate {
 	gpointer shell_view;  /* weak pointer */
 	ECalModel *model;
+	GCancellable *completed_cancellable; /* when processing completed tasks */
 
 	GtkTargetList *copy_target_list;
 	GtkTargetList *paste_target_list;
@@ -383,6 +384,12 @@ task_table_dispose (GObject *object)
 
 	priv = E_TASK_TABLE (object)->priv;
 
+	if (priv->completed_cancellable) {
+		g_cancellable_cancel (priv->completed_cancellable);
+		g_object_unref (priv->completed_cancellable);
+		priv->completed_cancellable = NULL;
+	}
+
 	if (priv->shell_view != NULL) {
 		g_object_remove_weak_pointer (
 			G_OBJECT (priv->shell_view), &priv->shell_view);
@@ -1474,6 +1481,8 @@ task_table_init (ETaskTable *task_table)
 	task_table->priv = G_TYPE_INSTANCE_GET_PRIVATE (
 		task_table, E_TYPE_TASK_TABLE, ETaskTablePrivate);
 
+	task_table->priv->completed_cancellable = NULL;
+
 	target_list = gtk_target_list_new (NULL, 0);
 	e_target_list_add_calendar_targets (target_list, 0);
 	task_table->priv->copy_target_list = target_list;
@@ -1634,62 +1643,79 @@ e_task_table_get_paste_target_list (ETaskTable *task_table)
 }
 
 static void
-hide_completed_rows (ECalModel *model,
-                     GList *clients_list,
-                     gchar *hide_sexp,
-                     GPtrArray *comp_objects)
+task_table_get_object_list_async (GList *clients_list,
+				  const gchar *sexp,
+				  GCancellable *cancellable,
+				  GAsyncReadyCallback callback,
+				  gpointer callback_data)
 {
 	GList *l;
-	GSList *m, *objects;
-	ECalClient *client;
-	gint pos;
-	gboolean changed = FALSE;
 
 	for (l = clients_list; l != NULL; l = l->next) {
-		GError *error = NULL;
+		ECalClient *client = l->data;
 
-		client = l->data;
+		e_cal_client_get_object_list (
+			client, sexp, cancellable,
+			callback, callback_data);
+	}
+}
 
-		e_cal_client_get_object_list_sync (
-			client, hide_sexp, &objects, NULL, &error);
+static void
+hide_completed_rows_ready (GObject *source_object,
+			   GAsyncResult *result,
+			   gpointer user_data)
+{
+	ECalModel *model = user_data;
+	GSList *m, *objects;
+	gboolean changed = FALSE;
+	gint pos;
+	GPtrArray *comp_objects;
+	GError *error = NULL;
 
-		if (error != NULL) {
-			g_warning (
-				"%s: Could not get the objects: %s",
-				G_STRFUNC, error->message);
-			g_error_free (error);
-			continue;
-		}
+	if (!e_cal_client_get_object_list_finish (E_CAL_CLIENT (source_object), result, &objects, &error)) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+		    !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
+			ESource *source = e_client_get_source (E_CLIENT (source_object));
 
-		for (m = objects; m; m = m->next) {
-			ECalModelComponent *comp_data;
-			ECalComponentId *id;
-			ECalComponent *comp = e_cal_component_new ();
-
-			e_cal_component_set_icalcomponent (
-				comp, icalcomponent_new_clone (m->data));
-			id = e_cal_component_get_id (comp);
-
-			comp_data = e_cal_model_get_component_for_uid (model, id);
-			if (comp_data != NULL) {
-				e_table_model_pre_change (E_TABLE_MODEL (model));
-				pos = get_position_in_array (
-					comp_objects, comp_data);
-				e_table_model_row_deleted (
-					E_TABLE_MODEL (model), pos);
-				changed = TRUE;
-
-				if (g_ptr_array_remove (comp_objects, comp_data))
-					g_object_unref (comp_data);
-			}
-			e_cal_component_free_id (id);
-			g_object_unref (comp);
+			g_debug ("%s: Could not get the objects from '%s': %s",
+				G_STRFUNC,
+				source ? e_source_peek_name (source) : "???",
+				error ? error->message : "Uknown error");
 		}
+		g_clear_error (&error);
+		return;
+	}
 
-		g_slist_foreach (objects, (GFunc) icalcomponent_free, NULL);
-		g_slist_free (objects);
+	comp_objects = e_cal_model_get_object_array (model);
+	g_return_if_fail (comp_objects != NULL);
+
+	for (m = objects; m; m = m->next) {
+		ECalModelComponent *comp_data;
+		ECalComponentId *id;
+		ECalComponent *comp = e_cal_component_new ();
+
+		e_cal_component_set_icalcomponent (
+			comp, icalcomponent_new_clone (m->data));
+		id = e_cal_component_get_id (comp);
+
+		comp_data = e_cal_model_get_component_for_uid (model, id);
+		if (comp_data != NULL) {
+			e_table_model_pre_change (E_TABLE_MODEL (model));
+			pos = get_position_in_array (
+				comp_objects, comp_data);
+			e_table_model_row_deleted (
+				E_TABLE_MODEL (model), pos);
+			changed = TRUE;
+
+			if (g_ptr_array_remove (comp_objects, comp_data))
+				g_object_unref (comp_data);
+		}
+		e_cal_component_free_id (id);
+		g_object_unref (comp);
 	}
 
+	e_cal_client_free_icalcomp_slist (objects);
+
 	if (changed) {
 		/* To notify about changes, because in call of
 		 * row_deleted there are still all events. */
@@ -1698,68 +1724,71 @@ hide_completed_rows (ECalModel *model,
 }
 
 static void
-show_completed_rows (ECalModel *model,
-                     GList *clients_list,
-                     gchar *show_sexp,
-                     GPtrArray *comp_objects)
+show_completed_rows_ready (GObject *source_object,
+			   GAsyncResult *result,
+			   gpointer user_data)
 {
-	GList *l;
-	GSList *m, *objects;
 	ECalClient *client;
+	ECalModel *model = user_data;
+	GSList *m, *objects;
+	GPtrArray *comp_objects;
+	GError *error = NULL;
 
-	for (l = clients_list; l != NULL; l = l->next) {
-		GError *error = NULL;
+	if (!e_cal_client_get_object_list_finish (E_CAL_CLIENT (source_object), result, &objects, &error)) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+		    !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
+			ESource *source = e_client_get_source (E_CLIENT (source_object));
 
-		client = l->data;
+			g_debug ("%s: Could not get the objects from '%s': %s",
+				G_STRFUNC,
+				source ? e_source_peek_name (source) : "???",
+				error ? error->message : "Uknown error");
+		}
+		g_clear_error (&error);
+		return;
+	}
 
-		e_cal_client_get_object_list_sync (
-			client, show_sexp, &objects, NULL, &error);
+	client = E_CAL_CLIENT (source_object);
+	g_return_if_fail (client != NULL);
 
-		if (error != NULL) {
-			g_warning (
-				"%s: Could not get the objects: %s",
-				G_STRFUNC, error->message);
-			g_error_free (error);
-			continue;
-		}
+	comp_objects = e_cal_model_get_object_array (model);
+	g_return_if_fail (comp_objects != NULL);
 
-		for (m = objects; m; m = m->next) {
-			ECalModelComponent *comp_data;
-			ECalComponentId *id;
-			ECalComponent *comp = e_cal_component_new ();
-
-			e_cal_component_set_icalcomponent (
-				comp, icalcomponent_new_clone (m->data));
-			id = e_cal_component_get_id (comp);
-
-			if (!(e_cal_model_get_component_for_uid (model, id))) {
-				e_table_model_pre_change (E_TABLE_MODEL (model));
-				comp_data = g_object_new (
-					E_TYPE_CAL_MODEL_COMPONENT, NULL);
-				comp_data->client = g_object_ref (client);
-				comp_data->icalcomp =
-					icalcomponent_new_clone (m->data);
-				e_cal_model_set_instance_times (
-					comp_data,
-					e_cal_model_get_timezone (model));
-				comp_data->dtstart = NULL;
-				comp_data->dtend = NULL;
-				comp_data->due = NULL;
-				comp_data->completed = NULL;
-				comp_data->color = NULL;
-
-				g_ptr_array_add (comp_objects, comp_data);
-				e_table_model_row_inserted (
-					E_TABLE_MODEL (model),
-					comp_objects->len - 1);
-			}
-			e_cal_component_free_id (id);
-			g_object_unref (comp);
-		}
+	for (m = objects; m; m = m->next) {
+		ECalModelComponent *comp_data;
+		ECalComponentId *id;
+		ECalComponent *comp = e_cal_component_new ();
 
-		g_slist_foreach (objects, (GFunc) icalcomponent_free, NULL);
-		g_slist_free (objects);
+		e_cal_component_set_icalcomponent (
+			comp, icalcomponent_new_clone (m->data));
+		id = e_cal_component_get_id (comp);
+
+		if (!(e_cal_model_get_component_for_uid (model, id))) {
+			e_table_model_pre_change (E_TABLE_MODEL (model));
+			comp_data = g_object_new (
+				E_TYPE_CAL_MODEL_COMPONENT, NULL);
+			comp_data->client = g_object_ref (client);
+			comp_data->icalcomp =
+				icalcomponent_new_clone (m->data);
+			e_cal_model_set_instance_times (
+				comp_data,
+				e_cal_model_get_timezone (model));
+			comp_data->dtstart = NULL;
+			comp_data->dtend = NULL;
+			comp_data->due = NULL;
+			comp_data->completed = NULL;
+			comp_data->color = NULL;
+
+			g_ptr_array_add (comp_objects, comp_data);
+			e_table_model_row_inserted (
+				E_TABLE_MODEL (model),
+				comp_objects->len - 1);
+		}
+		e_cal_component_free_id (id);
+		g_object_unref (comp);
 	}
+
+	e_cal_client_free_icalcomp_slist (objects);
 }
 
 /* Returns the current time, for the ECellDateEdit items.
@@ -1806,18 +1835,18 @@ e_task_table_process_completed_tasks (ETaskTable *task_table,
                                       gboolean config_changed)
 {
 	ECalModel *model;
-	static GMutex *mutex = NULL;
+	GCancellable *cancellable;
 	gchar *hide_sexp, *show_sexp;
-	GPtrArray *comp_objects = NULL;
 
-	if (!mutex)
-		mutex = g_mutex_new ();
+	if (task_table->priv->completed_cancellable) {
+		g_cancellable_cancel (task_table->priv->completed_cancellable);
+		g_object_unref (task_table->priv->completed_cancellable);
+	}
 
-	g_mutex_lock (mutex);
+	task_table->priv->completed_cancellable = g_cancellable_new ();
+	cancellable = task_table->priv->completed_cancellable;
 
 	model = e_task_table_get_model (task_table);
-	comp_objects = e_cal_model_get_object_array (model);
-
 	hide_sexp = calendar_config_get_hide_completed_tasks_sexp (TRUE);
 	show_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
 
@@ -1827,15 +1856,16 @@ e_task_table_process_completed_tasks (ETaskTable *task_table,
 
 	/* Delete rows from model*/
 	if (hide_sexp) {
-		hide_completed_rows (model, clients_list, hide_sexp, comp_objects);
+		task_table_get_object_list_async (clients_list, hide_sexp, cancellable,
+			hide_completed_rows_ready, model);
 	}
 
 	/* Insert rows into model */
 	if (config_changed) {
-		show_completed_rows (model, clients_list, show_sexp, comp_objects);
+		task_table_get_object_list_async (clients_list, show_sexp, cancellable,
+			show_completed_rows_ready, model);
 	}
 
 	g_free (hide_sexp);
 	g_free (show_sexp);
-	g_mutex_unlock (mutex);
 }
diff --git a/modules/calendar/e-task-shell-view-private.c b/modules/calendar/e-task-shell-view-private.c
index 3467c12..9164947 100644
--- a/modules/calendar/e-task-shell-view-private.c
+++ b/modules/calendar/e-task-shell-view-private.c
@@ -46,14 +46,17 @@ task_shell_view_model_row_appended_cb (ETaskShellView *task_shell_view,
 	e_task_shell_sidebar_add_source (task_shell_sidebar, source);
 }
 
-static void
-task_shell_view_process_completed_tasks (ETaskShellView *task_shell_view)
+static gboolean
+task_shell_view_process_completed_tasks (gpointer user_data)
 {
+	ETaskShellView *task_shell_view = user_data;
 	ETaskShellContent *task_shell_content;
 	ETaskShellSidebar *task_shell_sidebar;
 	ETaskTable *task_table;
 	GList *clients;
 
+	task_shell_view->priv->update_completed_timeout = 0;
+
 	task_shell_content = task_shell_view->priv->task_shell_content;
 	task_table = e_task_shell_content_get_task_table (task_shell_content);
 
@@ -67,6 +70,18 @@ task_shell_view_process_completed_tasks (ETaskShellView *task_shell_view)
 	e_shell_view_execute_search (E_SHELL_VIEW (task_shell_view));
 
 	g_list_free (clients);
+
+	return FALSE;
+}
+
+static void
+task_shell_view_schedule_process_completed_tasks (ETaskShellView *task_shell_view)
+{
+	if (task_shell_view->priv->update_completed_timeout)
+		g_source_remove (task_shell_view->priv->update_completed_timeout);
+
+	task_shell_view->priv->update_completed_timeout =
+		g_timeout_add_seconds (1, task_shell_view_process_completed_tasks, task_shell_view);
 }
 
 static void
@@ -345,15 +360,15 @@ e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
 	/* Hide Completed Tasks (enable/units/value) */
 	g_signal_connect_object (
 		shell_settings, "notify::cal-hide-completed-tasks",
-		G_CALLBACK (task_shell_view_process_completed_tasks),
+		G_CALLBACK (task_shell_view_schedule_process_completed_tasks),
 		task_shell_view, G_CONNECT_SWAPPED);
 	g_signal_connect_object (
 		shell_settings, "notify::cal-hide-completed-tasks-units",
-		G_CALLBACK (task_shell_view_process_completed_tasks),
+		G_CALLBACK (task_shell_view_schedule_process_completed_tasks),
 		task_shell_view, G_CONNECT_SWAPPED);
 	g_signal_connect_object (
 		shell_settings, "notify::cal-hide-completed-tasks-value",
-		G_CALLBACK (task_shell_view_process_completed_tasks),
+		G_CALLBACK (task_shell_view_schedule_process_completed_tasks),
 		task_shell_view, G_CONNECT_SWAPPED);
 
 	e_task_shell_view_actions_init (task_shell_view);
@@ -392,6 +407,11 @@ e_task_shell_view_private_dispose (ETaskShellView *task_shell_view)
 		g_source_remove (priv->update_timeout);
 		priv->update_timeout = 0;
 	}
+
+	if (priv->update_completed_timeout > 0) {
+		g_source_remove (priv->update_completed_timeout);
+		priv->update_completed_timeout = 0;
+	}
 }
 
 void
diff --git a/modules/calendar/e-task-shell-view-private.h b/modules/calendar/e-task-shell-view-private.h
index ec2173f..66e6646 100644
--- a/modules/calendar/e-task-shell-view-private.h
+++ b/modules/calendar/e-task-shell-view-private.h
@@ -100,6 +100,7 @@ struct _ETaskShellViewPrivate {
 
 	EActivity *activity;
 	guint update_timeout;
+	guint update_completed_timeout;
 
 	guint confirm_purge : 1;
 };



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]