[gnome-builder] buffermanager: protect against double open race
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] buffermanager: protect against double open race
- Date: Wed, 9 Aug 2017 00:26:13 +0000 (UTC)
commit d30ab4285de50b3f3d6650c52c0b2f35d0f8b0fa
Author: Christian Hergert <chergert redhat com>
Date: Tue Aug 8 17:26:03 2017 -0700
buffermanager: protect against double open race
If the user double clicks in the project-tree, we could get into a race
condition where the first buffer is not yet registered. This adds a loading
hashtable so we can check if the buffer is being loaded.
If so, we ignore the second request by synthesizing an error upfront.
libide/buffers/ide-buffer-manager.c | 45 +++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)
---
diff --git a/libide/buffers/ide-buffer-manager.c b/libide/buffers/ide-buffer-manager.c
index d17ec57..69c9f4d 100644
--- a/libide/buffers/ide-buffer-manager.c
+++ b/libide/buffers/ide-buffer-manager.c
@@ -57,6 +57,7 @@ struct _IdeBufferManager
IdeBuffer *focus_buffer;
GtkSourceCompletionWords *word_completion;
GSettings *settings;
+ GHashTable *loading;
gsize max_file_size;
@@ -775,6 +776,27 @@ ide_buffer_manager__load_file_read_cb (GObject *object,
IDE_EXIT;
}
+static void
+ide_buffer_manager_load_task_completed (IdeBufferManager *self,
+ GParamSpec *pspec,
+ GTask *task)
+{
+ LoadState *state;
+
+ g_assert (IDE_IS_BUFFER_MANAGER (self));
+ g_assert (pspec != NULL);
+ g_assert (g_strcmp0 ("completed", pspec->name) == 0);
+ g_assert (G_IS_TASK (task));
+
+ state = g_task_get_task_data (task);
+ g_assert (state != NULL);
+ g_assert (state->file != NULL);
+ g_assert (IDE_IS_FILE (state->file));
+
+ if (self->loading != NULL)
+ g_hash_table_remove (self->loading, state->file);
+}
+
/**
* ide_buffer_manager_load_file_async:
* @progress: (out) (nullable): A location for an #IdeProgress or %NULL.
@@ -814,6 +836,17 @@ ide_buffer_manager_load_file_async (IdeBufferManager *self,
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_priority (task, G_PRIORITY_LOW);
+ g_task_set_source_tag (task, ide_buffer_manager_load_file_async);
+
+ if (g_hash_table_contains (self->loading, file))
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_BUSY,
+ "The file is already loading");
+ IDE_EXIT;
+ }
context = ide_object_get_context (IDE_OBJECT (self));
ide_context_hold_for_object (context, task);
@@ -871,6 +904,14 @@ ide_buffer_manager_load_file_async (IdeBufferManager *self,
g_task_set_task_data (task, state, load_state_free);
+ g_hash_table_insert (self->loading, g_object_ref (file), NULL);
+
+ g_signal_connect_object (task,
+ "notify::completed",
+ G_CALLBACK (ide_buffer_manager_load_task_completed),
+ self,
+ G_CONNECT_SWAPPED);
+
if (progress)
*progress = g_object_ref (state->progress);
@@ -1352,6 +1393,7 @@ ide_buffer_manager_finalize (GObject *object)
g_warning ("Not all buffers have been destroyed.");
g_clear_pointer (&self->buffers, g_ptr_array_unref);
+ g_clear_pointer (&self->loading, g_hash_table_unref);
g_clear_pointer (&self->timeouts, g_hash_table_unref);
g_clear_object (&self->settings);
@@ -1630,6 +1672,9 @@ ide_buffer_manager_init (IdeBufferManager *self)
self->timeouts = g_hash_table_new (g_direct_hash, g_direct_equal);
self->word_completion = g_object_new (IDE_TYPE_COMPLETION_WORDS, NULL);
self->settings = g_settings_new ("org.gnome.builder.editor");
+ self->loading = g_hash_table_new_full ((GHashFunc)ide_file_hash,
+ (GEqualFunc)ide_file_equal,
+ g_object_unref, NULL);
g_settings_bind (self->settings, "minimum-word-size", self->word_completion, "minimum-word-size",
G_SETTINGS_BIND_GET);
g_settings_bind (self->settings, "auto-save", self, "auto-save", G_SETTINGS_BIND_GET);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]