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:
Daemonratte 2026-09-01 11:28:00 +02:00
commit d0c833a8a7
3 changed files with 1198 additions and 733 deletions

View file

@ -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);

View file

@ -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