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):

Reply via email to