From 75113b7a6f4bc370918b2eba03ead9d007a01309 Mon Sep 17 00:00:00 2001 From: Yannis Gerlach <100762533+ygerlach@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:01:07 +0100 Subject: [PATCH] add io_nice for background rsync tasks --- src/Core/Main.vala | 11 ++--------- src/Utility/AsyncTask.vala | 15 ++++++++++++--- src/Utility/DeleteFileTask.vala | 5 ----- src/Utility/RsyncTask.vala | 5 ----- src/Utility/TeeJee.Process.vala | 19 ++++++++++++++++++- src/meson.build | 11 +++++++++-- src/vapi/ioprio.vapi | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 src/vapi/ioprio.vapi diff --git a/src/Core/Main.vala b/src/Core/Main.vala index 28f5bc2c..b334c55a 100644 --- a/src/Core/Main.vala +++ b/src/Core/Main.vala @@ -1659,11 +1659,7 @@ public class Main : GLib.Object{ task.delete_extra = true; task.delete_excluded = true; task.delete_after = false; - - if (app_mode.length > 0){ - // console mode - task.io_nice = true; - } + task.io_nice = (app_mode.length > 0); task.execute(); @@ -4020,10 +4016,7 @@ public class Main : GLib.Object{ task.delete_excluded = true; task.delete_after = false; - if (app_mode.length > 0){ - // console mode - task.io_nice = true; - } + task.io_nice = (app_mode.length > 0); task.dry_run = true; diff --git a/src/Utility/AsyncTask.vala b/src/Utility/AsyncTask.vala index cd51c5bf..e9f9421c 100644 --- a/src/Utility/AsyncTask.vala +++ b/src/Utility/AsyncTask.vala @@ -63,6 +63,8 @@ public abstract class AsyncTask : GLib.Object{ public int64 prg_count = 0; public int64 prg_count_total = 0; + public bool io_nice = true; // renice child processes to IDlE PRIO + // signals public signal void stdout_line_read(string line); public signal void stderr_line_read(string line); @@ -112,7 +114,7 @@ public abstract class AsyncTask : GLib.Object{ } protected abstract string build_script(); - + protected virtual bool begin() { status = AppStatus.RUNNING; @@ -131,20 +133,27 @@ public abstract class AsyncTask : GLib.Object{ timer = new GLib.Timer(); timer.start(); + GLib.SpawnChildSetupFunc? childsetup = null; + + if(this.io_nice) { + // change io prio of process, right before it execs + childsetup = () => TeeJee.ProcessHelper.ioprio_set(0, IoPrio.prioValue(IoPrio.PrioClass.IDLE, 0)); + } + // execute script file Process.spawn_async_with_pipes( working_dir, // working dir spawn_args, // argv spawn_env, // environment SpawnFlags.SEARCH_PATH, - null, // child_setup + childsetup, // child_setup out child_pid, out input_fd, out output_fd, out error_fd); log_debug("AsyncTask: child_pid: %d".printf(child_pid)); - + // create stream readers UnixOutputStream uos_in = new UnixOutputStream(input_fd, true); UnixInputStream uis_out = new UnixInputStream(output_fd, true); diff --git a/src/Utility/DeleteFileTask.vala b/src/Utility/DeleteFileTask.vala index 4d925858..6814440a 100644 --- a/src/Utility/DeleteFileTask.vala +++ b/src/Utility/DeleteFileTask.vala @@ -33,7 +33,6 @@ public class DeleteFileTask : AsyncTask{ // settings public string dest_path = ""; public bool verbose = true; - public bool io_nice = true; public bool use_rsync = false; //private @@ -80,10 +79,6 @@ public class DeleteFileTask : AsyncTask{ protected override string build_script() { var cmd = ""; - if (io_nice){ - //cmd += "ionice -c2 -n7 "; - } - if (use_rsync){ cmd += "rsync -aii"; diff --git a/src/Utility/RsyncTask.vala b/src/Utility/RsyncTask.vala index 57497b28..0019dda6 100644 --- a/src/Utility/RsyncTask.vala +++ b/src/Utility/RsyncTask.vala @@ -57,7 +57,6 @@ public class RsyncTask : AsyncTask{ public string source_path = ""; public string dest_path = ""; public bool verbose = true; - public bool io_nice = true; public bool dry_run = false; // regex @@ -186,10 +185,6 @@ public class RsyncTask : AsyncTask{ var cmd = "export LC_ALL=C.UTF-8\n"; - if (io_nice){ - //cmd += "ionice -c2 -n7 "; - } - cmd += "rsync -aii"; //if (!dry_run){ diff --git a/src/Utility/TeeJee.Process.vala b/src/Utility/TeeJee.Process.vala index 38ba1b61..ab07dec6 100644 --- a/src/Utility/TeeJee.Process.vala +++ b/src/Utility/TeeJee.Process.vala @@ -367,7 +367,9 @@ namespace TeeJee.ProcessHelper{ public Pid[] get_process_children (Pid parent_pid){ - /* Returns the list of child processes owned by a given process */ + /* Returns the list of child processes owned by a given process + This does not contain grand children + */ // no explicit check for the existence of /proc/ as this might be a time-of-check-time-of-use bug. File procfs = File.new_for_path("/proc/"); @@ -490,4 +492,19 @@ namespace TeeJee.ProcessHelper{ process_set_priority (procID, 5); } + + /* + Wrapper for the ioprio_set syscall (has no libc wrapper) + see: man 2 ioprio_set + + pid may be 0 (self) + prio shall be constructed using IoPrio.prioValue + */ + public static bool ioprio_set(Pid pid, int prio) { + bool success = 0 == IoPrio.syscall(IoPrio.SYS_ioprio_set, IoPrio.IOPRIO_WHO_PROCESS, pid, prio); + if(!success) { + log_debug("ioprio failed with errno: %i".printf(Posix.errno)); + } + return success; + } } diff --git a/src/meson.build b/src/meson.build index def2ce5d..aa825824 100644 --- a/src/meson.build +++ b/src/meson.build @@ -95,18 +95,25 @@ sources_app_gtk = files([ 'AppGtk.vala', ]) +vala_opts = [ + '--vapidir=' + meson.current_source_dir() / 'vapi', + '--pkg', 'ioprio' +] + timeshift = executable('timeshift', sources_app_console + sources_core + sources_utility, config_header, dependencies: dependencies, - install: true + install: true, + vala_args: vala_opts ) timeshift_gtk = executable('timeshift-gtk', sources_app_gtk + sources_core + sources_utility + sources_gtk, config_header, dependencies: dependencies, - install: true + install: true, + vala_args: vala_opts ) install_data( diff --git a/src/vapi/ioprio.vapi b/src/vapi/ioprio.vapi new file mode 100644 index 00000000..f55b1aea --- /dev/null +++ b/src/vapi/ioprio.vapi @@ -0,0 +1,33 @@ + +// iorpio syscalls + +[CCode (cprefix = "", lower_case_cprefix = "")] +namespace IoPrio { + [CCode (cheader_filename = "unistd.h", sentinel = "", feature_test_macro = "_DEFAULT_SOURCE")] + extern long syscall (long number, ...); + + [CCode (cheader_filename = "sys/syscall.h")] + public const int SYS_ioprio_get; + + [CCode (cheader_filename = "sys/syscall.h")] + public const int SYS_ioprio_set; + + [CCode (cheader_filename = "linux/ioprio.h")] + public const int IOPRIO_WHO_PROCESS; + + [CCode (cheader_filename = "linux/ioprio.h", cname = "int", cprefix = "IOPRIO_CLASS_")] + public enum PrioClass { + NONE, + RT, + BE, + IDLE, + INVALID, + } + + /* + construct a prio value using a PrioClass and a class specific data attribute + See man 2 ioprio_set for details + */ + [CCode (cheader_filename = "linux/ioprio.h", cname = "IOPRIO_PRIO_VALUE")] + extern int prioValue(PrioClass clas, int data); +}