search: Rework threading

Search uses GcalTimeline, an object that is meant to be single
threaded, in multple threads. That mostly worked okay because
GcalTimeline mostly relies on GcalCalendarMonitor, which is
heavily threaded. But, sometimes it failed.

Rework that to be single-threaded, and asynchronous.

Closes https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/981
Closes https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/987
Closes https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/988
This commit is contained in:
Georges Basile Stavracas Neto
2023-03-16 12:27:47 +00:00
parent f751803e07
commit daec07c9f6
3 changed files with 101 additions and 85 deletions
+35 -67
View File
@@ -22,20 +22,12 @@
#include "gcal-context.h"
#include "gcal-date-time-utils.h"
#include "gcal-debug.h"
#include "gcal-search-engine.h"
#include "gcal-search-model.h"
#include "gcal-timeline.h"
#include "gcal-timeline-subscriber.h"
typedef struct
{
GcalSearchEngine *engine;
gchar *query;
gint max_results;
GDateTime *range_start;
GDateTime *range_end;
} SearchData;
struct _GcalSearchEngine
{
GObject parent;
@@ -57,59 +49,10 @@ enum
static GParamSpec *properties [N_PROPS];
/*
* Auxiliary methods
*/
static void
search_data_free (gpointer data)
{
SearchData *search_data = data;
if (!data)
return;
gcal_clear_date_time (&search_data->range_start);
gcal_clear_date_time (&search_data->range_end);
g_clear_pointer (&search_data->query, g_free);
g_clear_object (&search_data->engine);
g_slice_free (SearchData, data);
}
/*
* Callbacks
*/
static void
search_func (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr (GcalSearchModel) model = NULL;
GcalSearchEngine *self;
SearchData *data;
self = GCAL_SEARCH_ENGINE (source_object);
data = (SearchData*) task_data;
model = gcal_search_model_new (cancellable,
data->max_results,
data->range_start,
data->range_end);
gcal_timeline_set_filter (self->timeline, data->query);
gcal_timeline_add_subscriber (self->timeline, GCAL_TIMELINE_SUBSCRIBER (model));
gcal_search_model_wait_for_hits (model, cancellable);
if (g_cancellable_is_cancelled (cancellable))
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
else
g_task_return_pointer (task, g_steal_pointer (&model), g_object_unref);
}
static void
on_manager_calendar_added_cb (GcalManager *manager,
GcalCalendar *calendar,
@@ -130,6 +73,31 @@ on_manager_calendar_removed_cb (GcalManager *manager,
gcal_timeline_remove_calendar (self->timeline, calendar);
}
static void
search_model_hits_cb (GObject *source,
GAsyncResult *result,
gpointer data)
{
g_autoptr (GError) error = NULL;
g_autoptr (GTask) task = data;
GcalSearchModel *search_model;
GCAL_ENTRY;
search_model = GCAL_SEARCH_MODEL (source);
gcal_search_model_wait_for_hits_finish (search_model, result, &error);
if (error)
{
g_task_return_error (task, g_steal_pointer (&error));
GCAL_RETURN ();
}
g_task_return_pointer (task, g_object_ref (search_model), NULL);
GCAL_EXIT;
}
/*
* GObject overrides
@@ -246,9 +214,11 @@ gcal_search_engine_search (GcalSearchEngine *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GcalSearchModel) model = NULL;
g_autoptr (GDateTime) range_start = NULL;
g_autoptr (GDateTime) range_end = NULL;
g_autoptr (GDateTime) now = NULL;
g_autoptr (GTask) task = NULL;
SearchData *data = NULL;
GTimeZone *timezone;
g_return_if_fail (GCAL_IS_SEARCH_ENGINE (self));
@@ -256,20 +226,18 @@ gcal_search_engine_search (GcalSearchEngine *self,
timezone = gcal_context_get_timezone (self->context);
now = g_date_time_new_now (timezone);
range_start = g_date_time_add_weeks (now, -1);
range_end = g_date_time_add_weeks (now, 3);
model = gcal_search_model_new (cancellable, max_results, range_start, range_end);
data = g_slice_new0 (SearchData);
data->engine = g_object_ref (self);
data->query = g_strdup (search_query);
data->max_results = max_results;
data->range_start = g_date_time_add_weeks (now, -1);
data->range_end = g_date_time_add_weeks (now, 3);
gcal_timeline_set_filter (self->timeline, search_query);
gcal_timeline_add_subscriber (self->timeline, GCAL_TIMELINE_SUBSCRIBER (model));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gcal_search_engine_search);
g_task_set_priority (task, G_PRIORITY_LOW);
g_task_set_task_data (task, data, (GDestroyNotify) search_data_free);
g_task_run_in_thread (task, search_func);
gcal_search_model_wait_for_hits (model, cancellable, search_model_hits_cb, g_object_ref (task));
}
GListModel*
+59 -17
View File
@@ -42,6 +42,9 @@ struct _GcalSearchModel
GDateTime *range_end;
GListModel *model;
GTimer *timer;
guint idle_id;
};
static void gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface);
@@ -58,6 +61,33 @@ G_DEFINE_TYPE_WITH_CODE (GcalSearchModel, gcal_search_model, G_TYPE_OBJECT,
* Callbacks
*/
static gboolean
check_for_search_hits_cb (gpointer user_data)
{
GcalSearchModel *self;
GTask *task;
task = G_TASK (user_data);
self = GCAL_SEARCH_MODEL (g_task_get_source_object (task));
if (g_task_return_error_if_cancelled (task))
goto stop_idle;
if (g_timer_elapsed (self->timer, NULL) >= WAIT_FOR_RESULTS_MS ||
g_list_model_get_n_items (self->model) >= MIN_RESULTS)
{
g_task_return_boolean (task, TRUE);
goto stop_idle;
}
return G_SOURCE_CONTINUE;
stop_idle:
g_clear_pointer (&self->timer, g_timer_destroy);
self->idle_id = 0;
return G_SOURCE_REMOVE;
}
static void
on_model_items_changed_cb (GListModel *model,
guint position,
@@ -208,6 +238,9 @@ gcal_search_model_finalize (GObject *object)
{
GcalSearchModel *self = (GcalSearchModel *)object;
g_assert (self->timer == NULL);
g_assert (self->idle_id == 0);
g_cancellable_cancel (self->cancellable);
gcal_clear_date_time (&self->range_start);
@@ -251,29 +284,38 @@ gcal_search_model_new (GCancellable *cancellable,
}
void
gcal_search_model_wait_for_hits (GcalSearchModel *self,
GCancellable *cancellable)
gcal_search_model_wait_for_hits (GcalSearchModel *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GMainContext) thread_context = NULL;
g_autoptr (GTimer) timer = NULL;
g_autoptr (GTask) task = NULL;
GCAL_ENTRY;
g_return_if_fail (GCAL_IS_SEARCH_MODEL (self));
g_assert (self->timer == NULL);
g_assert (self->idle_id == 0);
thread_context = g_main_context_ref_thread_default ();
timer = g_timer_new ();
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gcal_search_model_wait_for_hits);
g_task_set_priority (task, G_PRIORITY_LOW);
g_timer_start (timer);
while (g_list_model_get_n_items (self->model) < MIN_RESULTS &&
g_timer_elapsed (timer, NULL) < WAIT_FOR_RESULTS_MS)
{
if (g_cancellable_is_cancelled (cancellable))
break;
g_main_context_iteration (thread_context, FALSE);
}
self->timer = g_timer_new ();
self->idle_id = g_idle_add_full (G_PRIORITY_LOW,
check_for_search_hits_cb,
g_object_ref (task),
g_object_unref);
GCAL_EXIT;
}
gboolean
gcal_search_model_wait_for_hits_finish (GcalSearchModel *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GCAL_IS_SEARCH_MODEL (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
+7 -1
View File
@@ -33,6 +33,12 @@ GcalSearchModel* gcal_search_model_new (GCancellable
GDateTime *range_end);
void gcal_search_model_wait_for_hits (GcalSearchModel *self,
GCancellable *cancellable);
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean gcal_search_model_wait_for_hits_finish (GcalSearchModel *self,
GAsyncResult *result,
GError **error);
G_END_DECLS