gtkfilesystemmodel: make directory loading cooperative and race-safe
Rework GtkFileSystemModel around stable node IDs, sparse value caching and cooperative directory integration. Time-slice initial sorting, visibility publication and monitor replay so large directories do not block the main loop. Queue monitor changes during loading and prevent stale asynchronous monitor queries from resurrecting deleted or superseded files. Delay FileChooser model publication until loading and replay complete, and apply the initial sort directly to the browse model.
This commit is contained in:
parent
437b7949dd
commit
d0c833a8a7
3 changed files with 1198 additions and 733 deletions
|
|
@ -141,14 +141,12 @@ _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, c
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
|
||||
|
||||
#define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
|
||||
#define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
|
||||
#define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
|
||||
|
||||
#define MAX_LOADING_TIME 500
|
||||
|
||||
#define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")
|
||||
|
||||
|
|
@ -427,7 +425,6 @@ static void remove_bookmark_button_clicked_cb (GtkButton *button,
|
|||
|
||||
static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
|
||||
|
||||
static void load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state);
|
||||
static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
|
||||
|
||||
static void view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode);
|
||||
|
|
@ -1908,9 +1905,6 @@ gtk_file_chooser_default_finalize (GObject *object)
|
|||
search_clear_model (impl, FALSE);
|
||||
recent_clear_model (impl, FALSE);
|
||||
|
||||
/* stopping the load above should have cleared this */
|
||||
g_assert (impl->load_timeout_id == 0);
|
||||
|
||||
g_free (impl->preview_display_name);
|
||||
|
||||
if (impl->rename_idle)
|
||||
|
|
@ -11719,61 +11713,8 @@ load_set_model (GtkFileChooserDefault *impl)
|
|||
profile_end ("end", NULL);
|
||||
}
|
||||
|
||||
/* Timeout callback used when the loading timer expires */
|
||||
static gboolean
|
||||
load_timeout_cb (gpointer data)
|
||||
{
|
||||
GtkFileChooserDefault *impl;
|
||||
|
||||
profile_start ("start", NULL);
|
||||
|
||||
impl = GTK_FILE_CHOOSER_DEFAULT (data);
|
||||
g_assert (impl->load_state == LOAD_PRELOAD);
|
||||
g_assert (impl->load_timeout_id != 0);
|
||||
g_assert (impl->browse_files_model != NULL);
|
||||
|
||||
impl->load_timeout_id = 0;
|
||||
impl->load_state = LOAD_LOADING;
|
||||
|
||||
load_set_model (impl);
|
||||
|
||||
profile_end ("end", NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
|
||||
static void
|
||||
load_setup_timer (GtkFileChooserDefault *impl)
|
||||
{
|
||||
g_assert (impl->load_timeout_id == 0);
|
||||
g_assert (impl->load_state != LOAD_PRELOAD);
|
||||
|
||||
impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
|
||||
impl->load_state = LOAD_PRELOAD;
|
||||
}
|
||||
|
||||
/* Removes the load timeout; changes the impl->load_state to the specified value. */
|
||||
static void
|
||||
load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state)
|
||||
{
|
||||
if (impl->load_timeout_id != 0)
|
||||
{
|
||||
g_assert (impl->load_state == LOAD_PRELOAD);
|
||||
|
||||
g_source_remove (impl->load_timeout_id);
|
||||
impl->load_timeout_id = 0;
|
||||
}
|
||||
else
|
||||
g_assert (impl->load_state == LOAD_EMPTY ||
|
||||
impl->load_state == LOAD_LOADING ||
|
||||
impl->load_state == LOAD_FINISHED);
|
||||
|
||||
g_assert (new_load_state == LOAD_EMPTY ||
|
||||
new_load_state == LOAD_LOADING ||
|
||||
new_load_state == LOAD_FINISHED);
|
||||
impl->load_state = new_load_state;
|
||||
}
|
||||
|
||||
/* Selects the first row in the file list */
|
||||
static void
|
||||
|
|
@ -11992,15 +11933,8 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
|
|||
if (error)
|
||||
show_error_on_reading_current_folder (impl, error);
|
||||
|
||||
if (impl->load_state == LOAD_PRELOAD)
|
||||
{
|
||||
load_remove_timer (impl, LOAD_FINISHED);
|
||||
if (impl->load_state == LOAD_LOADING)
|
||||
load_set_model (impl);
|
||||
}
|
||||
else if (impl->load_state == LOAD_LOADING)
|
||||
{
|
||||
/* Nothing */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We can't g_assert_not_reached(), as something other than us may have
|
||||
|
|
@ -12010,8 +11944,6 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
|
|||
return;
|
||||
}
|
||||
|
||||
g_assert (impl->load_timeout_id == 0);
|
||||
|
||||
impl->load_state = LOAD_FINISHED;
|
||||
|
||||
thumbnail_file_list_clear_pixbuf_caches (impl);
|
||||
|
|
@ -12029,7 +11961,7 @@ static void
|
|||
stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
|
||||
gboolean remove_from_treeview)
|
||||
{
|
||||
load_remove_timer (impl, LOAD_EMPTY);
|
||||
impl->load_state = LOAD_EMPTY;
|
||||
|
||||
if (impl->browse_files_model)
|
||||
{
|
||||
|
|
@ -12625,12 +12557,15 @@ set_list_model (GtkFileChooserDefault *impl,
|
|||
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL);
|
||||
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL);
|
||||
gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL);
|
||||
set_sort_column (impl);
|
||||
gtk_tree_sortable_set_sort_column_id
|
||||
(GTK_TREE_SORTABLE (impl->browse_files_model),
|
||||
impl->sort_column,
|
||||
impl->sort_order);
|
||||
impl->list_sort_ascending = TRUE;
|
||||
g_signal_connect (impl->browse_files_model, "sort-column-changed",
|
||||
G_CALLBACK (list_sort_column_changed_cb), impl);
|
||||
|
||||
load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
|
||||
impl->load_state = LOAD_LOADING;
|
||||
|
||||
g_signal_connect (impl->browse_files_model, "finished-loading",
|
||||
G_CALLBACK (browse_files_model_finished_loading_cb), impl);
|
||||
|
|
|
|||
|
|
@ -122,8 +122,7 @@ struct _GtkFileChooserWidgetPrivate
|
|||
|
||||
typedef enum {
|
||||
LOAD_EMPTY, /* There is no model */
|
||||
LOAD_PRELOAD, /* Model is loading and a timer is running; model isn't inserted into the tree yet */
|
||||
LOAD_LOADING, /* Timeout expired, model is inserted into the tree, but not fully loaded yet */
|
||||
LOAD_LOADING, /* Model is loading; inserted into the view when loading finishes */
|
||||
LOAD_FINISHED /* Model is fully loaded and inserted into the tree */
|
||||
} LoadState;
|
||||
|
||||
|
|
@ -310,7 +309,6 @@ struct _GtkFileChooserDefault
|
|||
|
||||
LoadState load_state;
|
||||
ReloadState reload_state;
|
||||
guint load_timeout_id;
|
||||
|
||||
OperationMode operation_mode;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue