commit: 3f32f9683abed9b95febdf37d7c891a624b75e1a
Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sun Nov 9 18:40:54 2025 +0000
Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sun Nov 9 18:40:54 2025 +0000
URL: https://gitweb.gentoo.org/proj/steve.git/commit/?id=3f32f968
Add `--add-jobs` and `--delete-jobs` options
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
README.rst | 16 +++++++++-------
pyproject.toml | 1 -
steve.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 64 insertions(+), 11 deletions(-)
diff --git a/README.rst b/README.rst
index 54f7cce..205fd5f 100644
--- a/README.rst
+++ b/README.rst
@@ -86,13 +86,15 @@ Adjusting the job count at runtime
Due to the simplicity of the jobserver protocol, it is trivially
possible to adjust the job count at runtime.
-To reduce the number of available jobs, read an appropriate number
-of characters from the pipe. For example, to lower the job count
-by 3 jobs, call::
+To add more jobs to the jobserver, use ``--add-jobs`` with the same
+jobserver FIFO path::
- read -L 3 < ${path_to_fifo}
+ steve -a ${jobs} ${path_to_fifo}
-Similarly, to increase the number jobs, write an appropriate number
-of (any) characters to the pipe::
+To remove them, use ``--remove-jobs``. This will block until
+all the requested jobs are removed. If the number is larger than total
+number of jobs, it will block forever.
- printf ... > ${path_to_fifo}
+::
+
+ steve -d ${jobs} ${path_to_fifo}
diff --git a/pyproject.toml b/pyproject.toml
index b272a92..f5efec8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -58,7 +58,6 @@ extend-select = [
"ARG",
"ERA",
"PGH",
- "PL",
"PERF",
"FURB",
"RUF",
diff --git a/steve.py b/steve.py
index 12466cc..f15dcc4 100755
--- a/steve.py
+++ b/steve.py
@@ -27,6 +27,13 @@ if TYPE_CHECKING:
__version__ = "0.0.1"
+def positive_int(value: str) -> int:
+ ret = int(value)
+ if ret <= 0:
+ raise argparse.ArgumentTypeError("must be positive")
+ return ret
+
+
def exit_sig_handler(signum: int, _: FrameType | None) -> None:
print(f"Exiting on {signal.Signals(signum).name}")
raise SystemExit(0)
@@ -53,6 +60,22 @@ def main() -> None:
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
argp = argparse.ArgumentParser()
+
+ commands = argp.add_mutually_exclusive_group()
+ commands.add_argument(
+ "-a",
+ "--add-jobs",
+ type=positive_int,
+ help="Add the specified number of jobs to the running jobserver",
+ )
+ commands.add_argument(
+ "-d",
+ "--delete-jobs",
+ "--remove-jobs",
+ type=positive_int,
+ help="Remove the specified number of jobs to the running jobserver",
+ )
+
argp.add_argument(
"-c",
"--control-fifo",
@@ -62,7 +85,7 @@ def main() -> None:
argp.add_argument(
"-j",
"--jobs",
- type=int,
+ type=positive_int,
help="Number of jobs to allow (default: nproc)",
)
argp.add_argument(
@@ -72,13 +95,42 @@ def main() -> None:
)
args = argp.parse_args()
+ if args.add_jobs is not None:
+ if args.add_jobs <= 0:
+ argp.error("--add-jobs must be larger than 0")
+ with args.path.open("wb") as job_file:
+ job_file.write(b"." * args.add_jobs)
+ print(f"{args.add_jobs} job tokens added to {args.path}")
+ return
+
+ if args.delete_jobs:
+ # first, remove as many jobs as we can without blocking
+ job_fd = os.open(args.path, os.O_RDONLY | os.O_NONBLOCK)
+ try:
+ removed = len(os.read(job_fd, args.delete_jobs))
+ except BlockingIOError:
+ pass
+ else:
+ print(f"{removed} jobs removed from {args.path}")
+ args.delete_jobs -= removed
+ os.close(job_fd)
+
+ if args.delete_jobs == 0:
+ return
+
+ print("Waiting for more jobs (may block forever)")
+ with open(args.path, "rb") as job_file:
+ while args.delete_jobs > 0:
+ job_file.read(1)
+ args.delete_jobs -= 1
+ print(f"Job removed, {args.delete_jobs} left to remove")
+ return
+
if args.jobs is None:
try:
args.jobs = multiprocessing.cpu_count()
except NotImplementedError:
argp.error("Cannot determine CPU count, please specify --jobs")
- if args.jobs <= 0:
- argp.error("--jobs must be larger than 0")
with contextlib.ExitStack() as context_managers:
context_managers.enter_context(hold_fifo(args.path))