use std::ffi::OsStr;
use std::process::Command;
#[cfg(unix)]
use std::process::Stdio;
#[cfg(all(unix, not(target_os = "macos")))]
use std::sync::LazyLock;
#[cfg(all(unix, not(target_os = "macos")))]
static HAS_IONICE: LazyLock<bool> = LazyLock::new(|| which::which("ionice").is_ok());
pub fn lower_current_process() {
#[cfg(unix)]
{
let pid = std::process::id().to_string();
let quiet = |mut cmd: Command| {
let _ = cmd
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
};
#[cfg(target_os = "macos")]
{
let mut cmd = Command::new("/usr/sbin/taskpolicy");
cmd.args(["-b", "-p", &pid]);
quiet(cmd);
}
#[cfg(not(target_os = "macos"))]
{
let mut renice = Command::new("renice");
renice.args(["-n", "19", "-p", &pid]);
quiet(renice);
if *HAS_IONICE {
let mut ionice = Command::new("ionice");
ionice.args(["-c", "3", "-p", &pid]);
quiet(ionice);
}
}
}
}
pub fn command(program: impl AsRef<OsStr>, lower: bool) -> Command {
if !lower {
return Command::new(program);
}
#[cfg(target_os = "macos")]
{
let mut cmd = Command::new("/usr/sbin/taskpolicy");
cmd.arg("-b").arg(program);
cmd
}
#[cfg(all(unix, not(target_os = "macos")))]
{
linux_low_priority_command(program.as_ref(), *HAS_IONICE)
}
#[cfg(not(unix))]
{
Command::new(program)
}
}
#[cfg(all(unix, not(target_os = "macos")))]
fn linux_low_priority_command(program: &OsStr, has_ionice: bool) -> Command {
if has_ionice {
let mut cmd = Command::new("ionice");
cmd.args(["-c", "3", "--", "nice", "-n", "19", "--"])
.arg(program);
cmd
} else {
let mut cmd = Command::new("nice");
cmd.arg("-n").arg("19").arg("--").arg(program);
cmd
}
}
#[cfg(test)]
mod tests {
use super::*;
fn args_of(cmd: &Command) -> Vec<&str> {
cmd.get_args().map(|a| a.to_str().unwrap()).collect()
}
#[test]
fn command_no_lower_returns_bare() {
let cmd = command("echo", false);
assert_eq!(cmd.get_program(), "echo");
assert!(args_of(&cmd).is_empty());
}
#[cfg(target_os = "macos")]
#[test]
fn command_lower_wraps_in_taskpolicy() {
let cmd = command("echo", true);
assert_eq!(cmd.get_program(), "/usr/sbin/taskpolicy");
assert_eq!(args_of(&cmd), ["-b", "echo"]);
}
#[cfg(all(unix, not(target_os = "macos")))]
#[test]
fn linux_wrap_with_ionice() {
let cmd = linux_low_priority_command(OsStr::new("echo"), true);
assert_eq!(cmd.get_program(), "ionice");
assert_eq!(
args_of(&cmd),
["-c", "3", "--", "nice", "-n", "19", "--", "echo"]
);
}
#[cfg(all(unix, not(target_os = "macos")))]
#[test]
fn linux_wrap_without_ionice() {
let cmd = linux_low_priority_command(OsStr::new("echo"), false);
assert_eq!(cmd.get_program(), "nice");
assert_eq!(args_of(&cmd), ["-n", "19", "--", "echo"]);
}
#[cfg(not(unix))]
#[test]
fn command_lower_noop_on_non_unix() {
let cmd = command("echo", true);
assert_eq!(cmd.get_program(), "echo");
assert!(args_of(&cmd).is_empty());
}
#[test]
fn lower_current_process_does_not_panic() {
lower_current_process();
}
}