commit: 1eab62aea7a76ba4b0628eaaa1361d6aeabe3862 Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Sun Nov 9 03:06:26 2025 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Sun Nov 9 03:06:26 2025 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=1eab62ae
PORTAGE_NICENESS/IONICE_COMMAND/SCHEDULING_POLICY: forkserver support Set PORTAGE_NICENESS/IONICE_COMMAND/SCHEDULING_POLICY on both the main and forkerver pids. Report an error if the forkserver pid cannot be determined (from _forkserver._forkserver_pid attribute). Bug: https://bugs.gentoo.org/965763 Signed-off-by: Zac Medico <zmedico <AT> gentoo.org> lib/_emerge/actions.py | 123 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 33 deletions(-) diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py index 2bbe58dde9..15e8621c80 100644 --- a/lib/_emerge/actions.py +++ b/lib/_emerge/actions.py @@ -1,8 +1,9 @@ -# Copyright 1999-2024 Gentoo Authors +# Copyright 1999-2025 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import collections import logging +import multiprocessing import operator import platform import re @@ -3076,48 +3077,101 @@ def config_protect_check(trees): def apply_priorities(settings): - ionice(settings) - nice(settings) - set_scheduling_policy(settings) + config_vars = [] + if "PORTAGE_NICENESS" in settings: + config_vars.append("PORTAGE_NICENESS") + if "PORTAGE_IONICE_COMMAND" in settings: + config_vars.append("PORTAGE_IONICE_COMMAND") + if "PORTAGE_SCHEDULING_POLICY" in settings: + config_vars.append("PORTAGE_SCHEDULING_POLICY") + + if not config_vars: + return + pids = [("main", portage.getpid())] + if multiprocessing.get_start_method() == "forkserver": -def nice(settings): - try: - os.nice(int(settings.get("PORTAGE_NICENESS", "0"))) - except (OSError, ValueError) as e: - out = portage.output.EOutput() - out.eerror( - f"Failed to change nice value to '{settings.get('PORTAGE_NICENESS', '0')}'" - ) - out.eerror(f"{str(e)}\n") + def _get_forkserver_pid(): + try: + return multiprocessing.forkserver._forkserver._forkserver_pid + except AttributeError: + return None + + forkserver_pid = _get_forkserver_pid() + if not isinstance(forkserver_pid, int): + # force forkserver launch + portage.process.spawn(["true"]) + forkserver_pid = _get_forkserver_pid() + + if not isinstance(forkserver_pid, int): + out = portage.output.EOutput() + out.eerror("Could not find forkserver pid") + out.eerror( + f"Configuration variable(s) will not be applied: {' '.join(config_vars)}" + ) + else: + pids.append(("forkserver", forkserver_pid)) + ionice(settings, pids) + nice(settings, pids) + set_scheduling_policy(settings, pids) -def ionice(settings): + +def nice(settings, pids): + + for name, pid in pids: + cmd = f"renice -n {settings.get('PORTAGE_NICENESS', '0')} {pid}".split() + try: + with open(os.devnull, "wb", 0) as dev_null: + rval = portage.process.spawn( + cmd, env=os.environ, fd_pipes={1: dev_null.fileno()} + ) + except portage.exception.CommandNotFound: + if "PORTAGE_NICENESS" in settings: + out = portage.output.EOutput() + out.eerror( + f"PORTAGE_NICENESS not applied because the renice command was not found" + ) + return + if rval != os.EX_OK: + out = portage.output.EOutput() + out.eerror(f"renice command returned {rval} for {name} pid {pid}") + + +def ionice(settings, pids): ionice_cmd = settings.get("PORTAGE_IONICE_COMMAND") if ionice_cmd: ionice_cmd = shlex.split(ionice_cmd) if not ionice_cmd: return - variables = {"PID": str(portage.getpid())} - cmd = [varexpand(x, mydict=variables) for x in ionice_cmd] + errors = [] + for name, pid in pids: + variables = {"PID": str(pid)} + cmd = [varexpand(x, mydict=variables) for x in ionice_cmd] - try: - rval = portage.process.spawn(cmd, env=os.environ) - except portage.exception.CommandNotFound: - # The OS kernel probably doesn't support ionice, - # so return silently. - return + try: + rval = portage.process.spawn(cmd, env=os.environ) + except portage.exception.CommandNotFound: + # The OS kernel probably doesn't support ionice, + # so return silently. + return - if rval != os.EX_OK: + if rval != os.EX_OK: + errors.append( + f"PORTAGE_IONICE_COMMAND returned {rval} for {name} pid {pid}" + ) + + if errors: out = portage.output.EOutput() - out.eerror(f"PORTAGE_IONICE_COMMAND returned {rval}") + for line in errors: + out.eerror(line) out.eerror( "See the make.conf(5) man page for PORTAGE_IONICE_COMMAND usage instructions." ) -def set_scheduling_policy(settings): +def set_scheduling_policy(settings, pids): scheduling_policy = settings.get("PORTAGE_SCHEDULING_POLICY") scheduling_priority = settings.get("PORTAGE_SCHEDULING_PRIORITY") @@ -3158,15 +3212,18 @@ def set_scheduling_policy(settings): ) return os.EX_USAGE - try: - os.sched_setscheduler( - portage.getpid(), policy, os.sched_param(scheduling_priority) - ) - except OSError as e: - out.eerror(f"Unable to apply PORTAGE_SCHEDULING_POLICY: {e}") - return os.EX_UNAVAILABLE + returncode = os.EX_OK - return os.EX_OK + for name, pid in pids: + try: + os.sched_setscheduler(pid, policy, os.sched_param(scheduling_priority)) + except OSError as e: + out.eerror( + f"Unable to apply PORTAGE_SCHEDULING_POLICY to {name} pid {pid}: {e}" + ) + returncode |= os.EX_UNAVAILABLE + + return returncode def setconfig_fallback(root_config):
