From 37b13a1a3b773824491b4d09d73b9849955cd828 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 9 Jul 2026 11:42:25 +0200 Subject: [PATCH] Move image save operation off the main thread Required for libvips/libvips#5165. --- src/gtkutil.c | 33 --------------------------------- src/gtkutil.h | 5 ----- src/progress.c | 4 ---- src/saveoptions.c | 37 ++++++++++++++++++++++++++++--------- 4 files changed, 28 insertions(+), 51 deletions(-) diff --git a/src/gtkutil.c b/src/gtkutil.c index fc76100..c08c2f9 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -23,11 +23,6 @@ #include "package.h" -// set when we're processing some events to update the GUI ... used to sanity -// check eg. reduce (we mustn't call reduce from inside the nested loop, -// it'll crash into the main reduce thread) -gboolean in_update = FALSE; - void set_glabel(GtkWidget *label, const char *fmt, ...) { @@ -255,34 +250,6 @@ get_state_int(GtkWidget *from, const char *name) return g_variant_get_int32(state); } -/* A 'safe' way to run a few events. - */ -void -process_events(void) -{ - /* Max events we process before signalling a timeout. Without this we - * can get stuck in event loops in some circumstances. - */ - static const int max_events = 100; - - /* Block too much recursion. 0 is from the top-level, 1 is from a - * callback, we don't want any more than that. - */ - if (!in_update && - g_main_depth() < 2) { - int n; - - in_update = TRUE; - - for (n = 0; n < max_events && - g_main_context_iteration(NULL, FALSE); - n++) - ; - - in_update = FALSE; - } -} - static gboolean block_scroll_cb(GtkEventControllerScroll *self, gdouble dx, gdouble dy, gpointer user_data) diff --git a/src/gtkutil.h b/src/gtkutil.h index 5959bb3..abfcc1b 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -58,9 +58,6 @@ #define PARENT_CLASS_DYNAMIC(OBJECT) \ (g_type_class_peek(g_type_parent(G_TYPE_FROM_INSTANCE(OBJECT)))) -// TRUE for in a nested mainloop -extern gboolean in_update; - void set_glabel(GtkWidget *label, const char *fmt, ...); void set_glabel1(GtkWidget *label, const char *fmt, ...); void set_gentryv(GtkWidget *edit, const char *fmt, va_list ap); @@ -85,8 +82,6 @@ gboolean get_state_bool(GtkWidget *from, const char *name); double get_state_double(GtkWidget *from, const char *name); int get_state_int(GtkWidget *from, const char *name); -void process_events(void); - void block_scroll(GtkWidget *widget); gboolean widget_should_animate(GtkWidget *widget); diff --git a/src/progress.c b/src/progress.c index b1df935..671aca4 100644 --- a/src/progress.c +++ b/src/progress.c @@ -150,8 +150,6 @@ progress_emit_begin(Progress *progress) static gboolean progress_emit_update(Progress *progress) { - process_events(); - gboolean cancel = FALSE; g_signal_emit(progress, progress_signals[SIG_UPDATE], 0, &cancel); @@ -240,8 +238,6 @@ progress_event_signal(ProgressEvent *event) { Progress *progress = progress_get(); - process_events(); - /* Throttle update events to 10Hz. */ if (event->signal == SIG_UPDATE) { diff --git a/src/saveoptions.c b/src/saveoptions.c index 01f0ba6..460cf33 100644 --- a/src/saveoptions.c +++ b/src/saveoptions.c @@ -295,18 +295,22 @@ save_options_set_argument(VipsObject *operation, } static void -save_options_ok_action(GSimpleAction *action, - GVariant *parameter, gpointer user_data) +save_options_build_thread(GTask *task, + gpointer source_object, gpointer task_data, GCancellable *cancellable) { - SaveOptions *options = SAVE_OPTIONS(user_data); + SaveOptions *options = SAVE_OPTIONS(source_object); - vips_argument_map(VIPS_OBJECT(options->save_operation), - save_options_set_argument, options, NULL); + g_task_return_boolean(task, + vips_cache_operation_buildp(&options->save_operation)); +} - // this will trigger the save and loop while we write ... the - // UI will stay live thanks to event processing in the eval - // handler - if (vips_cache_operation_buildp(&options->save_operation)) +static void +save_options_build_done(GObject *source, + GAsyncResult *result, gpointer user_data) +{ + SaveOptions *options = SAVE_OPTIONS(source); + + if (g_task_propagate_boolean(G_TASK(result), NULL)) save_options_error(options); else // everything worked, we can post success back to @@ -314,6 +318,21 @@ save_options_ok_action(GSimpleAction *action, gtk_window_destroy(GTK_WINDOW(options)); } +static void +save_options_ok_action(GSimpleAction *action, + GVariant *parameter, gpointer user_data) +{ + SaveOptions *options = SAVE_OPTIONS(user_data); + + vips_argument_map(VIPS_OBJECT(options->save_operation), + save_options_set_argument, options, NULL); + + g_autoptr(GTask) task = g_task_new(options, NULL, + save_options_build_done, NULL); + + g_task_run_in_thread(task, save_options_build_thread); +} + static void save_options_cancel_action(GSimpleAction *action, GVariant *parameter, gpointer user_data)