commit:     9040f400a65f6739918269c0e4d7ebf626fac811
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Thu Nov 13 03:19:20 2025 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Thu Nov 13 06:36:41 2025 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=9040f400

_prepare_self_update: update sys.path for forkserver or spawn

For multiprocessing forkserver and spawn start methods, sys.path is
inherited from the main process, so it needs to include portage._pym_path
where _prepare_self_update has created a backup of modules for the
running version of portage.

Bug: https://bugs.gentoo.org/965976
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>

 lib/portage/package/ebuild/doebuild.py | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/lib/portage/package/ebuild/doebuild.py 
b/lib/portage/package/ebuild/doebuild.py
index 53d70a9391..e83dfc0291 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -15,6 +15,7 @@ import platform
 import pwd
 import re
 import shlex
+import signal
 import stat
 import subprocess
 import sys
@@ -3422,6 +3423,38 @@ def _prepare_self_update(settings):
     for dir_path in (base_path_tmp, portage._bin_path, portage._pym_path):
         os.chmod(dir_path, 0o755)
 
+    # Update sys.path used to unpickle child process arguments for
+    # multiprocessing forkserver and spawn start methods (bug 965976).
+    sys.path.insert(0, portage._pym_path)
+
+    if multiprocessing.get_start_method() == "forkserver":
+
+        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):
+                writemsg(
+                    "!!! Failed to locate forkserver pid for sys.path 
update\n",
+                    noiselevel=-1,
+                )
+
+            # If a forkserver was successfully launched then it
+            # inherited our sys.path update and there is no need
+            # to kill it.
+        else:
+            # Kill forkserver in order to force a sys.path update,
+            # and a new forkserver will launch on demand.
+            os.kill(forkserver_pid, signal.SIGTERM)
+
 
 def _handle_self_update(settings, vardb):
     if installation.TYPE != installation.TYPES.SYSTEM:

Reply via email to