From 5da21ceee424eb238278bdec258b0c6d8725ae21 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 2 Aug 2011 15:23:52 +0200 Subject: Bug #655190 - Sluggish performance interacting with calendar/tasks --- calendar/gui/dialogs/goto-dialog.c | 6 +- calendar/gui/dialogs/recurrence-page.c | 12 ++- calendar/gui/e-cal-model.c | 156 +++++++++++++++++++++++++-------- calendar/gui/e-cal-model.h | 3 +- calendar/gui/gnome-cal.c | 14 ++- calendar/gui/print.c | 11 ++- calendar/gui/tag-calendar.c | 31 ++++--- calendar/gui/tag-calendar.h | 4 +- 8 files changed, 176 insertions(+), 61 deletions(-) (limited to 'calendar') diff --git a/calendar/gui/dialogs/goto-dialog.c b/calendar/gui/dialogs/goto-dialog.c index 3ef0db31bf..7b8fffa430 100644 --- a/calendar/gui/dialogs/goto-dialog.c +++ b/calendar/gui/dialogs/goto-dialog.c @@ -52,6 +52,7 @@ typedef struct gint month_val; gint day_val; + GCancellable *cancellable; } GoToDialog; static GoToDialog *dlg = NULL; @@ -94,7 +95,7 @@ ecal_date_range_changed (ECalendarItem *calitem, gpointer user_data) model = gnome_calendar_get_model (dlg->gcal); client = e_cal_model_get_default_client (model); if (client) - tag_calendar_by_client (dlg->ecal, client); + tag_calendar_by_client (dlg->ecal, client, dlg->cancellable); } /* Event handler for day groups in the month item. A button press makes @@ -248,6 +249,7 @@ goto_dialog (GtkWindow *parent, GnomeCalendar *gcal) return; } dlg->gcal = gcal; + dlg->cancellable = g_cancellable_new (); model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); @@ -287,6 +289,8 @@ goto_dialog (GtkWindow *parent, GnomeCalendar *gcal) goto_today (dlg); g_object_unref (dlg->builder); + g_cancellable_cancel (dlg->cancellable); + g_object_unref (dlg->cancellable); g_free (dlg); dlg = NULL; } diff --git a/calendar/gui/dialogs/recurrence-page.c b/calendar/gui/dialogs/recurrence-page.c index bf8f1c191c..8e52250dfa 100644 --- a/calendar/gui/dialogs/recurrence-page.c +++ b/calendar/gui/dialogs/recurrence-page.c @@ -185,6 +185,8 @@ struct _RecurrencePagePrivate { /* This just holds some settings we need */ EMeetingStore *meeting_store; + + GCancellable *cancellable; }; @@ -266,7 +268,7 @@ preview_recur (RecurrencePage *rpage) fill_component (rpage, comp); tag_calendar_by_comp (E_CALENDAR (priv->preview_calendar), comp, - client, zone, TRUE, FALSE, FALSE); + client, zone, TRUE, FALSE, FALSE, priv->cancellable); g_object_unref (comp); } @@ -324,6 +326,12 @@ recurrence_page_dispose (GObject *object) priv->meeting_store = NULL; } + if (priv->cancellable) { + g_cancellable_cancel (priv->cancellable); + g_object_unref (priv->cancellable); + priv->cancellable = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (recurrence_page_parent_class)->dispose (object); } @@ -379,6 +387,8 @@ recurrence_page_init (RecurrencePage *rpage) { rpage->priv = G_TYPE_INSTANCE_GET_PRIVATE ( rpage, TYPE_RECURRENCE_PAGE, RecurrencePagePrivate); + + rpage->priv->cancellable = g_cancellable_new (); } /* get_widget handler for the recurrence page */ diff --git a/calendar/gui/e-cal-model.c b/calendar/gui/e-cal-model.c index 2b4aac732d..457455b88d 100644 --- a/calendar/gui/e-cal-model.c +++ b/calendar/gui/e-cal-model.c @@ -46,6 +46,7 @@ typedef struct { ECalClientView *view; gboolean do_query; + GCancellable *cancellable; } ECalModelClient; struct _ECalModelPrivate { @@ -398,6 +399,10 @@ cal_model_dispose (GObject *object) priv->clients = g_list_remove (priv->clients, client_data); g_object_unref (client_data->client); + if (client_data->cancellable) { + g_cancellable_cancel (client_data->cancellable); + g_object_unref (client_data->cancellable); + } if (client_data->view) g_object_unref (client_data->view); g_free (client_data); @@ -2226,14 +2231,18 @@ process_added (ECalClientView *view, const GSList *objects, ECalModel *model) ensure_dates_are_in_default_zone (model, l->data); if (e_cal_util_component_has_recurrences (l->data) && (priv->flags & E_CAL_MODEL_FLAGS_EXPAND_RECURRENCES)) { - RecurrenceExpansionData rdata; - - rdata.client = client; - rdata.view = view; - rdata.model = model; - rdata.icalcomp = l->data; - e_cal_client_generate_instances_for_object (rdata.client, l->data, priv->start, priv->end, - (ECalRecurInstanceFn) add_instance_cb, &rdata); + ECalModelClient *client_data = find_client_data (model, client); + + if (client_data) { + RecurrenceExpansionData *rdata = g_new0 (RecurrenceExpansionData, 1); + rdata->client = client; + rdata->view = view; + rdata->model = model; + rdata->icalcomp = l->data; + + e_cal_client_generate_instances_for_object (rdata->client, l->data, priv->start, priv->end, client_data->cancellable, + (ECalRecurInstanceFn) add_instance_cb, rdata, g_free); + } } else { e_table_model_pre_change (E_TABLE_MODEL (model)); @@ -2494,12 +2503,97 @@ client_view_complete_cb (ECalClientView *view, const GError *error, gpointer use e_cal_client_get_source_type (client)); } +struct get_view_data +{ + ECalModel *model; /* do not touch this, if cancelled */ + ECalModelClient *client_data; /* do not touch this, if cancelled */ + GCancellable *cancellable; + guint tries; +}; + +static void +free_get_view_data (struct get_view_data *gvd) +{ + if (!gvd) + return; + + g_object_unref (gvd->cancellable); + g_free (gvd); +} + +static gboolean retry_get_view_timeout_cb (gpointer user_data); + +static void +get_view_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) +{ + struct get_view_data *gvd = user_data; + GError *error = NULL; + ECalClientView *view = NULL; + + g_return_if_fail (source_object != NULL); + g_return_if_fail (result != NULL); + g_return_if_fail (gvd != NULL); + g_return_if_fail (gvd->model != NULL); + g_return_if_fail (gvd->client_data != NULL); + + if (!e_cal_client_get_view_finish (E_CAL_CLIENT (source_object), result, &view, &error)) { + if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) || + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_clear_error (&error); + + free_get_view_data (gvd); + return; + } + + if (gvd->tries < 10) { + gvd->tries++; + g_timeout_add (500, retry_get_view_timeout_cb, gvd); + return; + } + + g_debug ("%s: Failed to get view: %s", G_STRFUNC, error ? error->message : "Unknown error"); + + g_clear_error (&error); + } else { + gvd->client_data->view = view; + + g_signal_connect (gvd->client_data->view, "objects-added", G_CALLBACK (client_view_objects_added_cb), gvd->model); + g_signal_connect (gvd->client_data->view, "objects-modified", G_CALLBACK (client_view_objects_modified_cb), gvd->model); + g_signal_connect (gvd->client_data->view, "objects-removed", G_CALLBACK (client_view_objects_removed_cb), gvd->model); + g_signal_connect (gvd->client_data->view, "progress", G_CALLBACK (client_view_progress_cb), gvd->model); + g_signal_connect (gvd->client_data->view, "complete", G_CALLBACK (client_view_complete_cb), gvd->model); + + e_cal_client_view_start (gvd->client_data->view, &error); + + if (error) { + g_debug ("%s: Failed to start view: %s", G_STRFUNC, error->message); + g_error_free (error); + } + } + + free_get_view_data (gvd); +} + +static gboolean +retry_get_view_timeout_cb (gpointer user_data) +{ + struct get_view_data *gvd = user_data; + + if (g_cancellable_is_cancelled (gvd->cancellable)) { + free_get_view_data (gvd); + return FALSE; + } + + e_cal_client_get_view (gvd->client_data->client, gvd->model->priv->full_sexp, gvd->cancellable, get_view_cb, gvd); + + return FALSE; +} + static void update_e_cal_view_for_client (ECalModel *model, ECalModelClient *client_data) { ECalModelPrivate *priv; - GError *error = NULL; - gint tries = 0; + struct get_view_data *gvd; priv = model->priv; @@ -2522,34 +2616,20 @@ update_e_cal_view_for_client (ECalModel *model, ECalModelClient *client_data) if (!client_data->do_query) return; - try_again: - if (!e_cal_client_get_view_sync (client_data->client, priv->full_sexp, &client_data->view, NULL, &error)) { - if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY) && tries != 10) { - tries++; - /*TODO chose an optimal value */ - g_usleep (500); - g_clear_error (&error); - goto try_again; - } - - g_warning (G_STRLOC ": Unable to get query, %s", error ? error->message : "Unknown error"); - if (error) - g_error_free (error); - - return; + if (client_data->cancellable) { + g_cancellable_cancel (client_data->cancellable); + g_object_unref (client_data->cancellable); } - g_signal_connect (client_data->view, "objects-added", G_CALLBACK (client_view_objects_added_cb), model); - g_signal_connect (client_data->view, "objects-modified", G_CALLBACK (client_view_objects_modified_cb), model); - g_signal_connect (client_data->view, "objects-removed", G_CALLBACK (client_view_objects_removed_cb), model); - g_signal_connect (client_data->view, "progress", G_CALLBACK (client_view_progress_cb), model); - g_signal_connect (client_data->view, "complete", G_CALLBACK (client_view_complete_cb), model); + client_data->cancellable = g_cancellable_new (); - e_cal_client_view_start (client_data->view, &error); - if (error) { - g_debug ("%s: Failed to start view: %s", G_STRFUNC, error->message); - g_error_free (error); - } + gvd = g_new0 (struct get_view_data, 1); + gvd->client_data = client_data; + gvd->model = model; + gvd->tries = 0; + gvd->cancellable = g_object_ref (client_data->cancellable); + + e_cal_client_get_view (client_data->client, priv->full_sexp, gvd->cancellable, get_view_cb, gvd); } void @@ -3353,12 +3433,12 @@ e_cal_model_component_get_type (void) } /** - * e_cal_model_generate_instances + * e_cal_model_generate_instances_sync * * cb function is not called with cb_data, but with ECalModelGenerateInstancesData which contains cb_data */ void -e_cal_model_generate_instances (ECalModel *model, time_t start, time_t end, +e_cal_model_generate_instances_sync (ECalModel *model, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data) { ECalModelGenerateInstancesData mdata; @@ -3372,7 +3452,7 @@ e_cal_model_generate_instances (ECalModel *model, time_t start, time_t end, mdata.cb_data = cb_data; if (comp_data->instance_start < end && comp_data->instance_end > start) - e_cal_client_generate_instances_for_object (comp_data->client, comp_data->icalcomp, start, end, cb, &mdata); + e_cal_client_generate_instances_for_object_sync (comp_data->client, comp_data->icalcomp, start, end, cb, &mdata); } } diff --git a/calendar/gui/e-cal-model.h b/calendar/gui/e-cal-model.h index 766fd6286f..33ffcff52d 100644 --- a/calendar/gui/e-cal-model.h +++ b/calendar/gui/e-cal-model.h @@ -281,7 +281,8 @@ ECalModelComponent * const ECalComponentId *id); gchar * e_cal_model_date_value_to_string (ECalModel *model, gconstpointer value); -void e_cal_model_generate_instances (ECalModel *model, +void e_cal_model_generate_instances_sync + (ECalModel *model, time_t start, time_t end, ECalRecurInstanceFn cb, diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index 4bb430c018..03b5b6bfa6 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -131,6 +131,8 @@ struct _GnomeCalendarPrivate { /* Used in update_todo_view, to prevent interleaving when * called in separate thread. */ GMutex *todo_update_lock; + + GCancellable *cancellable; }; enum { @@ -775,7 +777,7 @@ dn_client_view_objects_added_cb (ECalClientView *view, const GSList *objects, gp tag_calendar_by_comp ( priv->date_navigator, comp, e_cal_client_view_get_client (view), - NULL, FALSE, TRUE, TRUE); + NULL, FALSE, TRUE, TRUE, priv->cancellable); g_object_unref (comp); } } @@ -1438,6 +1440,8 @@ gnome_calendar_init (GnomeCalendar *gcal) priv->visible_start = -1; priv->visible_end = -1; priv->updating = FALSE; + + priv->cancellable = g_cancellable_new (); } static void @@ -1492,6 +1496,12 @@ gnome_calendar_do_dispose (GObject *object) priv->update_marcus_bains_line_timeout = 0; } + if (priv->cancellable) { + g_cancellable_cancel (priv->cancellable); + g_object_unref (priv->cancellable); + priv->cancellable = NULL; + } + G_OBJECT_CLASS (gnome_calendar_parent_class)->dispose (object); } @@ -2238,7 +2248,7 @@ gnome_calendar_purge (GnomeCalendar *gcal, time_t older_than) pd.remove = TRUE; pd.older_than = older_than; - e_cal_client_generate_instances_for_object (client, m->data, + e_cal_client_generate_instances_for_object_sync (client, m->data, older_than, G_MAXINT32, check_instance_cb, &pd); diff --git a/calendar/gui/print.c b/calendar/gui/print.c index 702f281b5a..2c17b05c86 100644 --- a/calendar/gui/print.c +++ b/calendar/gui/print.c @@ -786,7 +786,7 @@ print_month_small (GtkPrintContext *context, GnomeCalendar *gcal, time_t month, sprintf (buf, "%d", day); /* this is a slow messy way to do this ... but easy ... */ - e_cal_model_generate_instances (gnome_calendar_get_model (gcal), now, + e_cal_model_generate_instances_sync (gnome_calendar_get_model (gcal), now, time_day_end_with_zone (now, zone), instance_cb, &found); @@ -1412,7 +1412,7 @@ print_day_details (GtkPrintContext *context, GnomeCalendar *gcal, time_t whence, pdi.zone = e_cal_model_get_timezone (model); /* Get the events from the server. */ - e_cal_model_generate_instances (model, start, end, print_day_details_cb, &pdi); + e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi); qsort (pdi.long_events->data, pdi.long_events->len, sizeof (EDayViewEvent), e_day_view_event_sort_func); qsort (pdi.events[0]->data, pdi.events[0]->len, @@ -1976,7 +1976,7 @@ print_week_summary (GtkPrintContext *context, GnomeCalendar *gcal, } /* Get the events from the server. */ - e_cal_model_generate_instances (model, + e_cal_model_generate_instances_sync (model, psi.day_starts[0], psi.day_starts[psi.days_shown], print_week_summary_cb, &psi); qsort (psi.events->data, psi.events->len, @@ -2496,7 +2496,7 @@ print_work_week_day_details (GtkPrintContext *context, GnomeCalendar *gcal, pdi.zone = e_cal_model_get_timezone (model); /* Get the events from the server. */ - e_cal_model_generate_instances (model, start, end, print_day_details_cb, &pdi); + e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi); qsort (pdi.long_events->data, pdi.long_events->len, sizeof (EDayViewEvent), e_day_view_event_sort_func); qsort (pdi.events[0]->data, pdi.events[0]->len, @@ -2689,8 +2689,7 @@ print_work_week_view (GtkPrintContext *context, GnomeCalendar *gcal, time_t date pdi.end_hour = e_cal_model_get_work_day_end_hour (model); pdi.zone = zone; - e_cal_model_generate_instances (model, start, end, - print_work_week_view_cb, &pdi); + e_cal_model_generate_instances_sync (model, start, end, print_work_week_view_cb, &pdi); print_work_week_background (context, gcal, date, &pdi, 0.0, width, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET, diff --git a/calendar/gui/tag-calendar.c b/calendar/gui/tag-calendar.c index dff1285587..affe2a3633 100644 --- a/calendar/gui/tag-calendar.c +++ b/calendar/gui/tag-calendar.c @@ -145,15 +145,17 @@ get_recur_events_italic (void) * tag_calendar_by_client: * @ecal: Calendar widget to tag. * @client: A calendar client object. + * @cancellable: A #GCancellable; can be %NULL * * Tags an #ECalendar widget with the events that occur in its current time * range. The occurrences are extracted from the specified calendar @client. **/ void tag_calendar_by_client (ECalendar *ecal, - ECalClient *client) + ECalClient *client, + GCancellable *cancellable) { - struct calendar_tag_closure c; + struct calendar_tag_closure *c; g_return_if_fail (E_IS_CALENDAR (ecal)); g_return_if_fail (E_IS_CAL_CLIENT (client)); @@ -165,14 +167,18 @@ tag_calendar_by_client (ECalendar *ecal, if (!e_client_is_opened (E_CLIENT (client))) return; - if (!prepare_tag (ecal, &c, NULL, TRUE)) + c = g_new0 (struct calendar_tag_closure, 1); + + if (!prepare_tag (ecal, c, NULL, TRUE)) { + g_free (c); return; + } - c.skip_transparent_events = TRUE; - c.recur_events_italic = get_recur_events_italic (); + c->skip_transparent_events = TRUE; + c->recur_events_italic = get_recur_events_italic (); e_cal_client_generate_instances ( - client, c.start_time, c.end_time, tag_calendar_cb, &c); + client, c->start_time, c->end_time, cancellable, tag_calendar_cb, c, g_free); } /* Resolves TZIDs for the recurrence generator, for when the comp is not on @@ -227,7 +233,8 @@ tag_calendar_by_comp (ECalendar *ecal, icaltimezone *display_zone, gboolean clear_first, gboolean comp_is_on_server, - gboolean can_recur_events_italic) + gboolean can_recur_events_italic, + GCancellable *cancellable) { struct calendar_tag_closure c; @@ -244,11 +251,15 @@ tag_calendar_by_comp (ECalendar *ecal, c.skip_transparent_events = FALSE; c.recur_events_italic = can_recur_events_italic && get_recur_events_italic (); - if (comp_is_on_server) + if (comp_is_on_server) { + struct calendar_tag_closure *closure = g_new0 (struct calendar_tag_closure, 1); + + *closure = c; + e_cal_client_generate_instances_for_object ( client, e_cal_component_get_icalcomponent (comp), - c.start_time, c.end_time, tag_calendar_cb, &c); - else + c.start_time, c.end_time, cancellable, tag_calendar_cb, closure, g_free); + } else e_cal_recur_generate_instances ( comp, c.start_time, c.end_time, tag_calendar_cb, &c, resolve_tzid_cb, diff --git a/calendar/gui/tag-calendar.h b/calendar/gui/tag-calendar.h index 2dc5bff176..c01e379b6e 100644 --- a/calendar/gui/tag-calendar.h +++ b/calendar/gui/tag-calendar.h @@ -30,10 +30,10 @@ #include #include -void tag_calendar_by_client (ECalendar *ecal, ECalClient *client); +void tag_calendar_by_client (ECalendar *ecal, ECalClient *client, GCancellable *cancellable); void tag_calendar_by_comp (ECalendar *ecal, ECalComponent *comp, ECalClient *client, icaltimezone *display_zone, gboolean clear_first, gboolean comp_is_on_server, - gboolean can_recur_events_italic); + gboolean can_recur_events_italic, GCancellable *cancellable); #endif -- cgit