From 94a0c9818a25bd236d37803dabb515a820a8261e Mon Sep 17 00:00:00 2001 From: Eva M Date: Sun, 5 Jul 2026 06:43:52 -0700 Subject: [PATCH] Use DexLimiter to limit concurrent io Begin using DexLimiter (from Dex 1.2) in places instead of the hacky macros from bz-util.h --- src/bz-application.c | 3 +- src/bz-async-texture.c | 111 +------------------------------------- src/bz-content-provider.c | 6 ++- src/bz-io.c | 29 +++++++--- src/bz-io.h | 3 ++ src/meson.build | 2 +- subprojects/.gitignore | 1 + subprojects/.wraplock | 0 subprojects/libdex.wrap | 10 ++++ 9 files changed, 46 insertions(+), 119 deletions(-) create mode 100644 subprojects/.gitignore create mode 100644 subprojects/.wraplock create mode 100644 subprojects/libdex.wrap diff --git a/src/bz-application.c b/src/bz-application.c index 43c4322b1..45c002f06 100644 --- a/src/bz-application.c +++ b/src/bz-application.c @@ -1472,7 +1472,8 @@ enumerate_disk_entries_fiber (GWeakRef *wr) io_data->cache = g_object_ref (self->cache); entries = dex_await_boxed ( - dex_scheduler_spawn ( + dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) enumerate_disk_io_fiber, diff --git a/src/bz-async-texture.c b/src/bz-async-texture.c index 8d7406729..a098a9298 100644 --- a/src/bz-async-texture.c +++ b/src/bz-async-texture.c @@ -20,8 +20,6 @@ #define G_LOG_DOMAIN "BAZAAR::ASYNC-TEXTURE" -#define MAX_CONCURRENT_IO 8 -#define MAX_CONCURRENT_GLYCIN 32 #define CACHE_INVALID_AGE (G_TIME_SPAN_DAY * 1) #define HTTP_TIMEOUT_SECONDS 5 #define MAX_LOAD_RETRIES 3 @@ -565,7 +563,8 @@ maybe_load (BzAsyncTexture *self) data->retries = self->retries; g_weak_ref_init (&data->self, self); - future = dex_scheduler_spawn ( + future = dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) load_fiber_work, @@ -580,18 +579,6 @@ maybe_load (BzAsyncTexture *self) static DexFuture * load_fiber_work (LoadData *data) { - static GMutex queueing_mutex = { 0 }; - - static guint concurrent_io = MAX_CONCURRENT_IO; - static guint io_queued[MAX_CONCURRENT_IO] = { 0 }; - static BzGuard *io_gates[MAX_CONCURRENT_IO] = { 0 }; - static GMutex io_mutexes[MAX_CONCURRENT_IO] = { 0 }; - - static guint concurrent_glycin = 0; - static guint glycin_queued[MAX_CONCURRENT_GLYCIN] = { 0 }; - static BzGuard *glycin_gates[MAX_CONCURRENT_GLYCIN] = { 0 }; - static GMutex glycin_mutexes[MAX_CONCURRENT_GLYCIN] = { 0 }; - GFile *source = data->source; char *source_uri = data->source_uri; GFile *cache_into = data->cache_into; @@ -601,7 +588,6 @@ load_fiber_work (LoadData *data) g_autoptr (GError) local_error = NULL; g_autoptr (GMutexLocker) locker = NULL; g_autoptr (BzGuard) slot_guard = NULL; - guint slot_queued = G_MAXUINT; gboolean is_http = FALSE; g_autoptr (GDateTime) now = NULL; g_autofree char *async_tex_data_path = NULL; @@ -609,69 +595,6 @@ load_fiber_work (LoadData *data) g_autoptr (GdkTexture) texture = NULL; g_autoptr (GlyFrame) frame = NULL; - locker = g_mutex_locker_new (&queueing_mutex); - if (concurrent_glycin == 0) - { - /* Ensure we don't overload the system with work; aim for # of logical - processors divided by 2 - - See: - https://github.com/bazaar-org/bazaar/issues/497 - https://docs.gtk.org/glib/func.get_num_processors.html - - Eva Thu, 23 Oct 2025 14:19:44 -0700 - */ - concurrent_glycin = MIN ( - MAX_CONCURRENT_GLYCIN, - MAX (1, g_get_num_processors () / 2)); - - g_debug ("Allowing %d concurrent texture glycin", concurrent_glycin); - } - g_clear_pointer (&locker, g_mutex_locker_free); - -#define FIND_LOCK(name, _idx) \ - G_STMT_START \ - { \ - locker = g_mutex_locker_new (&queueing_mutex); \ - \ - for (guint i = 0; i < concurrent_##name; i++) \ - { \ - if (name##_queued[i] < slot_queued) \ - { \ - slot_queued = name##_queued[i]; \ - (_idx) = i; \ - } \ - } \ - \ - name##_queued[(_idx)]++; \ - g_clear_pointer (&locker, g_mutex_locker_free); \ - } \ - G_STMT_END - -#define FINISH_LOCK(name, _idx) \ - G_STMT_START \ - { \ - locker = g_mutex_locker_new (&queueing_mutex); \ - name##_queued[(_idx)]--; \ - g_clear_pointer (&locker, g_mutex_locker_free); \ - } \ - G_STMT_END - -#define RATE_LIMIT_BEGIN(name) \ - G_STMT_START \ - { \ - guint _slot_index = 0; \ - \ - FIND_LOCK (name, _slot_index); \ - BZ_BEGIN_GUARD_WITH_CONTEXT (&slot_guard, \ - &name##_mutexes[_slot_index], \ - &name##_gates[_slot_index]); \ - FINISH_LOCK (name, _slot_index); \ - } \ - G_STMT_END - -#define RATE_LIMIT_END() bz_clear_guard (&slot_guard) - is_http = g_str_has_prefix (source_uri, "http"); now = g_date_time_new_now_utc (); if (cache_into != NULL) @@ -682,8 +605,6 @@ load_fiber_work (LoadData *data) if (cache_into != NULL) { - RATE_LIMIT_BEGIN (io); - if (g_file_query_exists (cache_into, NULL) && g_file_query_exists (async_tex_data_file, NULL)) { @@ -717,9 +638,6 @@ load_fiber_work (LoadData *data) { if (age_span < CACHE_INVALID_AGE) { - RATE_LIMIT_END (); - RATE_LIMIT_BEGIN (glycin); - loader = gly_loader_new (cache_into); /* We assume we exported this file, so uhhh it is safe to not use sandboxing, since it is faster :-) */ @@ -728,9 +646,6 @@ load_fiber_work (LoadData *data) image = gly_loader_load (loader, &local_error); if (image != NULL) frame = gly_image_next_frame (image, &local_error); - - RATE_LIMIT_END (); - RATE_LIMIT_BEGIN (io); } else g_debug ("Metadata file %s for cached texture at %s indicates this resource is too old (GTimeSpan: %zu), " @@ -763,8 +678,6 @@ load_fiber_work (LoadData *data) } } } - - RATE_LIMIT_END (); } if (frame == NULL) @@ -780,8 +693,6 @@ load_fiber_work (LoadData *data) parent = g_file_get_parent (cache_into); - RATE_LIMIT_BEGIN (io); - if (g_file_query_exists (parent, NULL)) { GFileType parent_type = G_FILE_TYPE_UNKNOWN; @@ -811,8 +722,6 @@ load_fiber_work (LoadData *data) return dex_future_new_for_error (g_steal_pointer (&local_error)); } } - - RATE_LIMIT_END (); } if (is_http) @@ -825,16 +734,12 @@ load_fiber_work (LoadData *data) g_autofree char *tmpl = NULL; g_autoptr (GFileIOStream) io = NULL; - RATE_LIMIT_BEGIN (io); - basename = g_file_get_basename (source); tmpl = g_strdup_printf ("XXXXXX-%s", basename); load_file = g_file_new_tmp (tmpl, &io, &local_error); if (load_file == NULL) return dex_future_new_for_error (g_steal_pointer (&local_error)); g_io_stream_close (G_IO_STREAM (io), NULL, NULL); - - RATE_LIMIT_END (); } result = dex_await ( @@ -853,8 +758,6 @@ load_fiber_work (LoadData *data) { if (cache_into != NULL) { - RATE_LIMIT_BEGIN (io); - result = g_file_copy ( source, cache_into, G_FILE_COPY_OVERWRITE | G_FILE_COPY_ALL_METADATA, @@ -862,16 +765,12 @@ load_fiber_work (LoadData *data) if (!result) return dex_future_new_for_error (g_steal_pointer (&local_error)); - RATE_LIMIT_END (); - load_file = g_object_ref (cache_into); } else load_file = g_object_ref (source); } - RATE_LIMIT_BEGIN (glycin); - loader = gly_loader_new (load_file); #ifdef SANDBOXED_LIBFLATPAK gly_loader_set_sandbox_selector (loader, GLY_SANDBOX_SELECTOR_NOT_SANDBOXED); @@ -888,8 +787,6 @@ load_fiber_work (LoadData *data) if (frame == NULL) return dex_future_new_for_error (g_steal_pointer (&local_error)); - RATE_LIMIT_END (); - if (async_tex_data_file != NULL) { g_autoptr (GVariantBuilder) builder = NULL; @@ -907,8 +804,6 @@ load_fiber_work (LoadData *data) variant = g_variant_builder_end (builder); bytes = g_variant_get_data_as_bytes (variant); - RATE_LIMIT_BEGIN (io); - output = g_file_replace ( async_tex_data_file, NULL, @@ -925,8 +820,6 @@ load_fiber_work (LoadData *data) g_output_stream_close (G_OUTPUT_STREAM (output), NULL, &local_error); } - RATE_LIMIT_END (); - if (local_error != NULL) g_warning ("Failed to write async-tex cache metadata to %s ;" "The image will be fully reloaded next time: %s", diff --git a/src/bz-content-provider.c b/src/bz-content-provider.c index ced5ac472..948964240 100644 --- a/src/bz-content-provider.c +++ b/src/bz-content-provider.c @@ -442,7 +442,8 @@ input_files_changed (BzContentProvider *self, init_data = input_init_data_new (); init_data->file = g_object_ref (additions[i]); - future = dex_scheduler_spawn ( + future = dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) input_init_fiber, @@ -616,7 +617,8 @@ commence_reload (InputTrackingData *data) load_data->file = g_file_new_for_path (data->path); load_data->parser = g_object_ref (self->parser); - future = dex_scheduler_spawn ( + future = dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) input_load_fiber, diff --git a/src/bz-io.c b/src/bz-io.c index d44570566..eb1cb2577 100644 --- a/src/bz-io.c +++ b/src/bz-io.c @@ -61,6 +61,17 @@ bz_get_io_scheduler (void) return scheduler; } +DexLimiter * +bz_get_io_limiter (void) +{ + static DexLimiter *limiter = NULL; + + if (g_once_init_enter_pointer (&limiter)) + g_once_init_leave_pointer (&limiter, dex_limiter_new (16)); + + return limiter; +} + void bz_reap_file (GFile *file) { @@ -220,7 +231,8 @@ DexFuture * bz_reap_file_dex (GFile *file) { dex_return_error_if_fail (G_IS_FILE (file)); - return dex_scheduler_spawn ( + return dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) reap_file_fiber, @@ -231,7 +243,8 @@ DexFuture * bz_reap_path_dex (const char *path) { dex_return_error_if_fail (path != NULL); - return dex_scheduler_spawn ( + return dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) reap_path_fiber, @@ -242,7 +255,8 @@ DexFuture * bz_reap_user_data_dex (const char *app_id) { dex_return_error_if_fail (app_id != NULL); - return dex_scheduler_spawn ( + return dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) reap_user_data_fiber, @@ -253,7 +267,8 @@ DexFuture * bz_reap_user_cache_dex (const char *app_id) { dex_return_error_if_fail (app_id != NULL); - return dex_scheduler_spawn ( + return dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) reap_user_cache_fiber, @@ -264,7 +279,8 @@ DexFuture * bz_get_user_sizes_dex (const char *app_id) { dex_return_error_if_fail (app_id != NULL); - return dex_scheduler_spawn ( + return dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) get_user_sizes_fiber, @@ -274,7 +290,8 @@ bz_get_user_sizes_dex (const char *app_id) DexFuture * bz_get_user_data_ids_dex (void) { - return dex_scheduler_spawn ( + return dex_limiter_run ( + bz_get_io_limiter (), bz_get_io_scheduler (), bz_get_dex_stack_size (), (DexFiberFunc) get_all_user_data_ids_fiber, diff --git a/src/bz-io.h b/src/bz-io.h index b7b5c932f..1d093160e 100644 --- a/src/bz-io.h +++ b/src/bz-io.h @@ -34,6 +34,9 @@ bz_dup_user_cache_path (const char *app_id); DexScheduler * bz_get_io_scheduler (void); +DexLimiter * +bz_get_io_limiter (void); + void bz_reap_file (GFile *file); diff --git a/src/meson.build b/src/meson.build index 2a2ddbf08..05270fc6d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,7 +3,7 @@ xmllint = find_program('xmllint', required: true) gtk_dep = dependency('gtk4', version: '>= 4.22.1') libadwaita_dep = dependency('libadwaita-1', version: '>= 1.8') -libdex_dep = dependency('libdex-1', version: '>= 1.0.0') +libdex_dep = dependency('libdex-1', version: '>= 1.2') flatpak_dep = dependency('flatpak', version: '>= 1.9') appstream_dep = dependency('appstream', version: '>= 1.0') xmlb_dep = dependency('xmlb', version: '>= 0.3.4') diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 000000000..f480d3c5f --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1 @@ +libdex/ diff --git a/subprojects/.wraplock b/subprojects/.wraplock new file mode 100644 index 000000000..e69de29bb diff --git a/subprojects/libdex.wrap b/subprojects/libdex.wrap new file mode 100644 index 000000000..eb6ccfeee --- /dev/null +++ b/subprojects/libdex.wrap @@ -0,0 +1,10 @@ +[wrap-git] +directory=libdex +url=https://gitlab.gnome.org/GNOME/libdex.git +push-url=ssh://git@ssh.gitlab.gnome.org:GNOME/libdex.git +revision=1.2.alpha +depth=1 + +[provide] +dependency_names=libdex-1 +libdex-1=libdex_dep