With Linux's autogroup scheduling feature (CONFIG_SCHED_AUTOGROUP)
setting a nice value on a per-process base has only an effect for
scheduling decisions relative to the other threads in the same
session (typically: the same terminal window). See the section "The
nice value and group scheduling" in the sched(7) man page.

Basically this means that portage "just" setting the nice value, has
no effect in presence of autogroup scheduling being active (which is
probably true for most (desktop) user systems).

This commit changes emerge to set the autogroup's nice value, instead
of the processes' nice value, in case autogroups are present (detected
by the existence of /proc/self/autogroup). The tricky part about
autogroup nice values is that we want restore the orignal nice value
once we are finished. As otherwise, the session, e.g. your terminal,
would continue using this value, and so would subsequently executed
processes. For that we use Python's atexit functinaly, to register a
function that will restore the orignal nice value of the autogroup.

Bug: https://bugs.gentoo.org/777492
Signed-off-by: Florian Schmaus <f...@geekplace.eu>
---
 lib/_emerge/actions.py | 46 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 39 insertions(+), 7 deletions(-)

diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py
index 239bf6f476d1..8f6e7c51b6dc 100644
--- a/lib/_emerge/actions.py
+++ b/lib/_emerge/actions.py
@@ -1,6 +1,7 @@
 # Copyright 1999-2020 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import atexit
 import collections
 import logging
 import operator
@@ -14,6 +15,7 @@ import textwrap
 import time
 import warnings
 from itertools import chain
+from pathlib import Path
 
 import portage
 portage.proxy.lazyimport.lazyimport(globals(),
@@ -2632,13 +2634,43 @@ def apply_priorities(settings):
        nice(settings)
 
 def nice(settings):
-       try:
-               os.nice(int(settings.get("PORTAGE_NICENESS", "0")))
-       except (OSError, ValueError) as e:
-               out = portage.output.EOutput()
-               out.eerror("Failed to change nice value to '%s'" % \
-                       settings.get("PORTAGE_NICENESS", "0"))
-               out.eerror("%s\n" % str(e))
+       autogroup_file = Path("/proc/self/autogroup")
+
+       nice_value : str = settings.get("PORTAGE_NICENESS", "0")
+
+       if not autogroup_file.is_file():
+               # Autogroup scheduling is not enabled on this system,
+               # continue using good ol' os.nice().
+               try:
+                       os.nice(int(nice_value))
+               except (OSError, ValueError) as e:
+                       out = portage.output.EOutput()
+                       out.eerror("Failed to change nice value to '%s'" % \
+                                  settings.get("PORTAGE_NICENESS", "0"))
+                       out.eerror("%s\n" % str(e))
+               return
+
+       with autogroup_file.open('r+') as f:
+               line = f.readline()
+               original_autogroup_nice_value = line.split(' ')[2]
+
+               # We need to restore the original nice value of the
+               # autogroup, as otherwise the session, e.g. the
+               # terminal where protage was executed in, would
+               # continue running with that value.
+               atexit.register(
+                       lambda value: autogroup_file.open('w').write(value),
+                       original_autogroup_nice_value
+               )
+
+               try:
+                       f.write(nice_value)
+               except (OSError) as e:
+                       out = portage.output.EOutput()
+                       out.eerror("Failed to change nice value to '%s'" % \
+                                  settings.get("PORTAGE_NICENESS", "0"))
+                       out.eerror("%s\n" % str(e))
+
 
 def ionice(settings):
 
-- 
2.26.3


Reply via email to