calendar-monitor: Operate in batches
Instead of smashing the main thread's mainloop with potentially hundreds or even thousands of idles that add, remove, or update individual events, pass arrays of events around.
This commit is contained in:
+132
-117
@@ -31,8 +31,8 @@
|
||||
typedef struct
|
||||
{
|
||||
GcalCalendarMonitor *monitor;
|
||||
GcalEvent *event;
|
||||
gchar *event_id;
|
||||
GPtrArray *events;
|
||||
GPtrArray *event_ids;
|
||||
gboolean complete;
|
||||
} IdleData;
|
||||
|
||||
@@ -81,9 +81,9 @@ struct _GcalCalendarMonitor
|
||||
} shared;
|
||||
};
|
||||
|
||||
static gboolean add_event_to_timeline_in_idle_cb (gpointer user_data);
|
||||
static gboolean update_event_in_idle_cb (gpointer user_data);
|
||||
static gboolean remove_event_from_timeline_in_idle_cb (gpointer user_data);
|
||||
static gboolean add_events_to_timeline_in_idle_cb (gpointer user_data);
|
||||
static gboolean update_events_in_idle_cb (gpointer user_data);
|
||||
static gboolean remove_events_from_timeline_in_idle_cb (gpointer user_data);
|
||||
static gboolean complete_in_idle_cb (gpointer user_data);
|
||||
|
||||
G_DEFINE_TYPE (GcalCalendarMonitor, gcal_calendar_monitor, G_TYPE_OBJECT)
|
||||
@@ -183,58 +183,58 @@ static void
|
||||
idle_data_free (IdleData *data)
|
||||
{
|
||||
g_clear_object (&data->monitor);
|
||||
g_clear_object (&data->event);
|
||||
g_clear_pointer (&data->event_id, g_free);
|
||||
g_clear_pointer (&data->events, g_ptr_array_unref);
|
||||
g_clear_pointer (&data->event_ids, g_ptr_array_unref);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
add_event_in_idle (GcalCalendarMonitor *self,
|
||||
GcalEvent *event)
|
||||
add_events_in_idle (GcalCalendarMonitor *self,
|
||||
GPtrArray *events)
|
||||
{
|
||||
IdleData *idle_data;
|
||||
|
||||
idle_data = g_new0 (IdleData, 1);
|
||||
idle_data->monitor = g_object_ref (self);
|
||||
idle_data->event = g_object_ref (event);
|
||||
idle_data->events = g_ptr_array_ref (events);
|
||||
|
||||
g_main_context_invoke_full (self->main_context,
|
||||
G_PRIORITY_DEFAULT_IDLE,
|
||||
add_event_to_timeline_in_idle_cb,
|
||||
add_events_to_timeline_in_idle_cb,
|
||||
idle_data,
|
||||
(GDestroyNotify) idle_data_free);
|
||||
}
|
||||
|
||||
static void
|
||||
update_event_in_idle (GcalCalendarMonitor *self,
|
||||
GcalEvent *event)
|
||||
update_events_in_idle (GcalCalendarMonitor *self,
|
||||
GPtrArray *events)
|
||||
{
|
||||
IdleData *idle_data;
|
||||
|
||||
idle_data = g_new0 (IdleData, 1);
|
||||
idle_data->monitor = g_object_ref (self);
|
||||
idle_data->event = g_object_ref (event);
|
||||
idle_data->events = g_ptr_array_ref (events);
|
||||
|
||||
g_main_context_invoke_full (self->main_context,
|
||||
G_PRIORITY_DEFAULT_IDLE,
|
||||
update_event_in_idle_cb,
|
||||
update_events_in_idle_cb,
|
||||
idle_data,
|
||||
(GDestroyNotify) idle_data_free);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_event_in_idle (GcalCalendarMonitor *self,
|
||||
const gchar *event_id)
|
||||
remove_events_in_idle (GcalCalendarMonitor *self,
|
||||
GPtrArray *events)
|
||||
{
|
||||
IdleData *idle_data;
|
||||
|
||||
idle_data = g_new0 (IdleData, 1);
|
||||
idle_data->monitor = g_object_ref (self);
|
||||
idle_data->event_id = g_strdup (event_id);
|
||||
idle_data->events = g_ptr_array_ref (events);
|
||||
|
||||
g_main_context_invoke_full (self->main_context,
|
||||
G_PRIORITY_DEFAULT_IDLE,
|
||||
remove_event_from_timeline_in_idle_cb,
|
||||
remove_events_from_timeline_in_idle_cb,
|
||||
idle_data,
|
||||
(GDestroyNotify) idle_data_free);
|
||||
}
|
||||
@@ -305,6 +305,7 @@ on_client_view_objects_added_cb (ECalClientView *view,
|
||||
GcalCalendarMonitor *self)
|
||||
{
|
||||
g_autoptr (GPtrArray) components_to_expand = NULL;
|
||||
g_autoptr (GPtrArray) events_to_add = NULL;
|
||||
g_autoptr (GcalRange) range = NULL;
|
||||
const GSList *l;
|
||||
gint i;
|
||||
@@ -315,6 +316,7 @@ on_client_view_objects_added_cb (ECalClientView *view,
|
||||
|
||||
maybe_init_event_arrays (self);
|
||||
components_to_expand = g_ptr_array_new ();
|
||||
events_to_add = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
for (l = objects; l; l = l->next)
|
||||
{
|
||||
@@ -359,13 +361,12 @@ on_client_view_objects_added_cb (ECalClientView *view,
|
||||
if (!self->monitor_thread.populated)
|
||||
g_ptr_array_add (self->monitor_thread.events_to_add, g_object_ref (event));
|
||||
else
|
||||
add_event_in_idle (self, event);
|
||||
g_ptr_array_add (events_to_add, g_object_ref (event));
|
||||
}
|
||||
|
||||
/* Recurrent events */
|
||||
if (components_to_expand->len > 0)
|
||||
{
|
||||
g_autoptr (GPtrArray) expanded_events = NULL;
|
||||
g_autoptr (GDateTime) range_start = NULL;
|
||||
g_autoptr (GDateTime) range_end = NULL;
|
||||
ECalClient *client;
|
||||
@@ -381,8 +382,6 @@ on_client_view_objects_added_cb (ECalClientView *view,
|
||||
range_start_time = g_date_time_to_unix (range_start);
|
||||
range_end_time = g_date_time_to_unix (range_end) - 1;
|
||||
|
||||
expanded_events = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
/* Generate the instances */
|
||||
for (i = 0; i < components_to_expand->len; i++)
|
||||
{
|
||||
@@ -398,10 +397,14 @@ on_client_view_objects_added_cb (ECalClientView *view,
|
||||
icomponent = g_ptr_array_index (components_to_expand, i);
|
||||
|
||||
recurrences_data.monitor = self;
|
||||
recurrences_data.expanded_events = expanded_events;
|
||||
|
||||
if (!self->monitor_thread.populated)
|
||||
recurrences_data.expanded_events = self->monitor_thread.events_to_add;
|
||||
else
|
||||
recurrences_data.expanded_events = events_to_add;
|
||||
|
||||
#if GCAL_ENABLE_TRACE
|
||||
old_size = expanded_events->len;
|
||||
old_size = recurrences_data.expanded_events->len;
|
||||
#endif
|
||||
|
||||
e_cal_client_generate_instances_for_object_sync (client,
|
||||
@@ -419,27 +422,16 @@ on_client_view_objects_added_cb (ECalClientView *view,
|
||||
GCAL_TRACE_MSG ("Component %s (%s) added %d instance(s) between %s",
|
||||
i_cal_component_get_summary (icomponent),
|
||||
i_cal_component_get_uid (icomponent),
|
||||
expanded_events->len - old_size,
|
||||
recurrences_data.expanded_events->len - old_size,
|
||||
range_str);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Process all these instances */
|
||||
for (i = 0; i < expanded_events->len; i++)
|
||||
{
|
||||
GcalEvent *event = g_ptr_array_index (expanded_events, i);
|
||||
|
||||
if (g_cancellable_is_cancelled (self->cancellable))
|
||||
return;
|
||||
|
||||
if (!self->monitor_thread.populated)
|
||||
g_ptr_array_add (self->monitor_thread.events_to_add, g_object_ref (event));
|
||||
else
|
||||
add_event_in_idle (self, event);
|
||||
}
|
||||
}
|
||||
|
||||
if (events_to_add->len > 0)
|
||||
add_events_in_idle (self, events_to_add);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
@@ -451,10 +443,10 @@ on_client_view_objects_modified_cb (ECalClientView *view,
|
||||
g_autoptr (GRWLockReaderLocker) reader_locker = NULL;
|
||||
g_autoptr (GHashTable) events_to_remove = NULL;
|
||||
g_autoptr (GPtrArray) components_to_expand = NULL;
|
||||
g_autoptr (GPtrArray) event_ids_to_remove = NULL;
|
||||
g_autoptr (GPtrArray) events_to_update = NULL;
|
||||
g_autoptr (GcalRange) range = NULL;
|
||||
GHashTableIter iter;
|
||||
const GSList *l;
|
||||
const gchar *aux;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
@@ -467,6 +459,7 @@ on_client_view_objects_modified_cb (ECalClientView *view,
|
||||
reader_locker = g_rw_lock_reader_locker_new (&self->shared.lock);
|
||||
components_to_expand = g_ptr_array_new ();
|
||||
events_to_remove = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
events_to_update = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
range = gcal_range_copy (self->shared.range);
|
||||
|
||||
for (l = objects; l; l = l->next)
|
||||
@@ -506,6 +499,9 @@ on_client_view_objects_modified_cb (ECalClientView *view,
|
||||
|
||||
if (!e_cal_component_id_get_rid (component_id))
|
||||
{
|
||||
GHashTableIter iter;
|
||||
const gchar *aux;
|
||||
|
||||
g_hash_table_iter_init (&iter, self->shared.events);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer*) &aux, NULL))
|
||||
{
|
||||
@@ -537,13 +533,14 @@ on_client_view_objects_modified_cb (ECalClientView *view,
|
||||
continue;
|
||||
}
|
||||
|
||||
update_event_in_idle (self, event);
|
||||
g_ptr_array_add (events_to_update, g_steal_pointer (&event));
|
||||
}
|
||||
|
||||
/* Recurrent events */
|
||||
if (components_to_expand->len > 0)
|
||||
{
|
||||
g_autoptr (GPtrArray) expanded_events = NULL;
|
||||
g_autoptr (GPtrArray) events_to_add = NULL;
|
||||
g_autoptr (GDateTime) range_start = NULL;
|
||||
g_autoptr (GDateTime) range_end = NULL;
|
||||
ECalClient *client;
|
||||
@@ -560,6 +557,7 @@ on_client_view_objects_modified_cb (ECalClientView *view,
|
||||
range_end_time = g_date_time_to_unix (range_end) - 1;
|
||||
|
||||
expanded_events = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
events_to_add = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
/* Generate the instances */
|
||||
for (guint i = 0; i < components_to_expand->len; i++)
|
||||
@@ -614,20 +612,26 @@ on_client_view_objects_modified_cb (ECalClientView *view,
|
||||
old_event = g_hash_table_lookup (self->shared.events, gcal_event_get_uid (event));
|
||||
if (old_event)
|
||||
{
|
||||
update_event_in_idle (self, event);
|
||||
g_hash_table_remove (events_to_remove, gcal_event_get_uid (event));
|
||||
g_ptr_array_add (events_to_update, g_steal_pointer (&event));
|
||||
}
|
||||
else
|
||||
{
|
||||
add_event_in_idle (self, event);
|
||||
g_ptr_array_add (events_to_add, g_steal_pointer (&event));
|
||||
}
|
||||
}
|
||||
|
||||
if (events_to_add->len > 0)
|
||||
add_events_in_idle (self, events_to_add);
|
||||
}
|
||||
|
||||
if (events_to_update->len > 0)
|
||||
update_events_in_idle (self, events_to_update);
|
||||
|
||||
/* Now remove lingering events */
|
||||
g_hash_table_iter_init (&iter, events_to_remove);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer*) &aux, NULL))
|
||||
remove_event_in_idle (self, aux);
|
||||
event_ids_to_remove = g_hash_table_steal_all_keys (events_to_remove);
|
||||
if (event_ids_to_remove->len > 0)
|
||||
remove_events_in_idle (self, event_ids_to_remove);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
@@ -637,10 +641,13 @@ on_client_view_objects_removed_cb (ECalClientView *view,
|
||||
const GSList *objects,
|
||||
GcalCalendarMonitor *self)
|
||||
{
|
||||
g_autoptr (GPtrArray) event_ids = NULL;
|
||||
const GSList *l;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
event_ids = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
for (l = objects; l; l = l->next)
|
||||
{
|
||||
g_autofree gchar *event_id = NULL;
|
||||
@@ -678,13 +685,16 @@ on_client_view_objects_removed_cb (ECalClientView *view,
|
||||
while (g_hash_table_iter_next (&iter, (gpointer*) &aux, NULL))
|
||||
{
|
||||
if (!g_str_equal (aux, event_id) && g_str_has_prefix (aux, event_id))
|
||||
remove_event_in_idle (self, aux);
|
||||
g_ptr_array_add (event_ids, g_strdup (aux));
|
||||
}
|
||||
}
|
||||
|
||||
remove_event_in_idle (self, event_id);
|
||||
g_ptr_array_add (event_ids, g_strdup (event_id));
|
||||
}
|
||||
|
||||
if (event_ids->len > 0)
|
||||
remove_events_in_idle (self, event_ids);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
@@ -701,22 +711,8 @@ on_client_view_complete_cb (ECalClientView *view,
|
||||
|
||||
events_to_add = g_steal_pointer (&self->monitor_thread.events_to_add);
|
||||
|
||||
/* Process all these instances */
|
||||
for (guint i = 0; events_to_add && i < events_to_add->len; i++)
|
||||
{
|
||||
GcalEvent *event;
|
||||
|
||||
if (g_cancellable_is_cancelled (self->cancellable))
|
||||
return;
|
||||
|
||||
event = g_ptr_array_index (events_to_add, i);
|
||||
|
||||
GCAL_TRACE_MSG ("Sending event '%s' (%s) to main thead's idle",
|
||||
gcal_event_get_summary (event),
|
||||
gcal_event_get_uid (event));
|
||||
|
||||
add_event_in_idle (self, event);
|
||||
}
|
||||
if (events_to_add)
|
||||
add_events_in_idle (self, events_to_add);
|
||||
|
||||
self->monitor_thread.populated = TRUE;
|
||||
|
||||
@@ -1022,86 +1018,105 @@ set_complete (GcalCalendarMonitor *self,
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
add_event_to_timeline_in_idle_cb (gpointer user_data)
|
||||
add_events_to_timeline_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
GcalEvent *event;
|
||||
GPtrArray *events;
|
||||
IdleData *idle_data;
|
||||
const gchar *event_id;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
idle_data = user_data;
|
||||
self = idle_data->monitor;
|
||||
event = idle_data->event;
|
||||
g_assert (idle_data->event_id == NULL);
|
||||
|
||||
event_id = gcal_event_get_uid (event);
|
||||
events = idle_data->events;
|
||||
g_assert (idle_data->event_ids == NULL);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
if (g_hash_table_insert (self->shared.events, g_strdup (event_id), g_object_ref (event)))
|
||||
g_signal_emit (self, signals[EVENT_ADDED], 0, event);
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_event_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
GcalEvent *old_event;
|
||||
GcalEvent *event;
|
||||
IdleData *idle_data;
|
||||
const gchar *event_id;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
idle_data = user_data;
|
||||
self = idle_data->monitor;
|
||||
event = idle_data->event;
|
||||
g_assert (idle_data->event_id == NULL);
|
||||
|
||||
event_id = gcal_event_get_uid (event);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
old_event = g_hash_table_lookup (self->shared.events, event_id);
|
||||
if (old_event)
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
g_hash_table_insert (self->shared.events, g_strdup (event_id), g_object_ref (event));
|
||||
g_signal_emit (self, signals[EVENT_UPDATED], 0, old_event, event);
|
||||
GcalEvent *event;
|
||||
const gchar *uid;
|
||||
|
||||
event = g_ptr_array_index (events, i);
|
||||
uid = gcal_event_get_uid (event);
|
||||
|
||||
if (g_hash_table_insert (self->shared.events, g_strdup (uid), g_object_ref (event)))
|
||||
g_signal_emit (self, signals[EVENT_ADDED], 0, event);
|
||||
}
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_event_from_timeline_in_idle_cb (gpointer user_data)
|
||||
update_events_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
g_autoptr (GcalEvent) event = NULL;
|
||||
g_autoptr (GPtrArray) events = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
IdleData *idle_data;
|
||||
gchar *event_id;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
idle_data = user_data;
|
||||
self = idle_data->monitor;
|
||||
g_assert (idle_data->event == NULL);
|
||||
event_id = idle_data->event_id;
|
||||
events = g_steal_pointer (&idle_data->events);
|
||||
g_assert (idle_data->event_ids == NULL);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
event = g_hash_table_lookup (self->shared.events, event_id);
|
||||
|
||||
if (event)
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
/* Keep the event alive until the signal is emitted */
|
||||
g_object_ref (event);
|
||||
GcalEvent *old_event;
|
||||
GcalEvent *event;
|
||||
const gchar *event_id;
|
||||
|
||||
g_hash_table_remove (self->shared.events, event_id);
|
||||
g_signal_emit (self, signals[EVENT_REMOVED], 0, event);
|
||||
event = g_ptr_array_index (events, i);
|
||||
event_id = gcal_event_get_uid (event);
|
||||
|
||||
old_event = g_hash_table_lookup (self->shared.events, event_id);
|
||||
if (old_event)
|
||||
{
|
||||
g_hash_table_insert (self->shared.events, g_strdup (event_id), g_object_ref (event));
|
||||
g_signal_emit (self, signals[EVENT_UPDATED], 0, old_event, event);
|
||||
}
|
||||
}
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_events_from_timeline_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
g_autoptr (GPtrArray) event_ids = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
IdleData *idle_data;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
idle_data = user_data;
|
||||
self = idle_data->monitor;
|
||||
event_ids = g_steal_pointer (&idle_data->event_ids);
|
||||
g_assert (idle_data->events == NULL);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
|
||||
for (guint i = 0; i < event_ids->len; i++)
|
||||
{
|
||||
g_autoptr (GcalEvent) event = NULL;
|
||||
const gchar *event_id;
|
||||
|
||||
event_id = g_ptr_array_index (event_ids, i);
|
||||
event = g_hash_table_lookup (self->shared.events, event_id);
|
||||
|
||||
if (event)
|
||||
{
|
||||
/* Keep the event alive until the signal is emitted */
|
||||
g_object_ref (event);
|
||||
|
||||
g_hash_table_remove (self->shared.events, event_id);
|
||||
g_signal_emit (self, signals[EVENT_REMOVED], 0, event);
|
||||
}
|
||||
}
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
@@ -1133,8 +1148,8 @@ complete_in_idle_cb (gpointer user_data)
|
||||
|
||||
idle_data = user_data;
|
||||
self = idle_data->monitor;
|
||||
g_assert (idle_data->event == NULL);
|
||||
g_assert (idle_data->event_id == NULL);
|
||||
g_assert (idle_data->events == NULL);
|
||||
g_assert (idle_data->event_ids == NULL);
|
||||
|
||||
set_complete (self, idle_data->complete);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user