aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2024-02-08 22:38:41 -0800
committerZac Medico <zmedico@gentoo.org>2024-02-09 00:19:00 -0800
commitc95fc64abf9698263090b3ffd4a056e989dd2be1 (patch)
treed80b59edddd71d254dcf480d215f7a65ebaafb3e /lib/portage
parentcheck_locale: Use multiprocessing.Process instead of os.fork() (diff)
downloadportage-c95fc64abf9698263090b3ffd4a056e989dd2be1.tar.gz
portage-c95fc64abf9698263090b3ffd4a056e989dd2be1.tar.bz2
portage-c95fc64abf9698263090b3ffd4a056e989dd2be1.zip
EbuildPhase: async_check_locale
Change config.environ() check_locale calls to async_check_locale calls in the EbuildPhase _async_start method in order to eliminate synchronous waiting for child processes in the main event loop thread. Bug: https://bugs.gentoo.org/923841 Signed-off-by: Zac Medico <zmedico@gentoo.org>
Diffstat (limited to 'lib/portage')
-rw-r--r--lib/portage/package/ebuild/config.py26
-rw-r--r--lib/portage/util/futures/_asyncio/__init__.py9
-rw-r--r--lib/portage/util/locale.py28
3 files changed, 39 insertions, 24 deletions
diff --git a/lib/portage/package/ebuild/config.py b/lib/portage/package/ebuild/config.py
index d7b0ca567..35c77486e 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -29,7 +29,6 @@ portage.proxy.lazyimport.lazyimport(
"portage.dbapi.vartree:vartree",
"portage.package.ebuild.doebuild:_phase_func_map",
"portage.util.compression_probe:_compressors",
- "portage.util.locale:check_locale,split_LC_ALL",
)
from portage import bsd_chflags, load_mod, os, selinux, _unicode_decode
from portage.const import (
@@ -3368,20 +3367,17 @@ class config:
mydict["EBUILD_PHASE_FUNC"] = phase_func
if eapi_attrs.posixish_locale:
- split_LC_ALL(mydict)
- mydict["LC_COLLATE"] = "C"
- # check_locale() returns None when check can not be executed.
- if check_locale(silent=True, env=mydict) is False:
- # try another locale
- for l in ("C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "C"):
- mydict["LC_CTYPE"] = l
- if check_locale(silent=True, env=mydict):
- # TODO: output the following only once
- # writemsg(_("!!! LC_CTYPE unsupported, using %s instead\n")
- # % mydict["LC_CTYPE"])
- break
- else:
- raise AssertionError("C locale did not pass the test!")
+ if mydict.get("LC_ALL"):
+ # Sometimes this method is called for processes
+ # that are not ebuild phases, so only raise
+ # AssertionError for actual ebuild phases.
+ if phase and phase not in ("clean", "cleanrm", "fetch"):
+ raise AssertionError(
+ f"LC_ALL={mydict['LC_ALL']} for posixish locale. It seems that split_LC_ALL was not called for phase {phase}?"
+ )
+ elif "LC_ALL" in mydict:
+ # Delete placeholder from split_LC_ALL.
+ del mydict["LC_ALL"]
if not eapi_attrs.exports_PORTDIR:
mydict.pop("PORTDIR", None)
diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py
index 8f1b8e827..e78686bc5 100644
--- a/lib/portage/util/futures/_asyncio/__init__.py
+++ b/lib/portage/util/futures/_asyncio/__init__.py
@@ -15,6 +15,7 @@ __all__ = (
"set_child_watcher",
"get_event_loop_policy",
"set_event_loop_policy",
+ "run",
"shield",
"sleep",
"Task",
@@ -106,6 +107,14 @@ def set_child_watcher(watcher):
return get_event_loop_policy().set_child_watcher(watcher)
+# Emulate run since it's the preferred python API.
+def run(coro):
+ return _safe_loop().run_until_complete(coro)
+
+
+run.__doc__ = _real_asyncio.run.__doc__
+
+
def create_subprocess_exec(*args, **kwargs):
"""
Create a subprocess.
diff --git a/lib/portage/util/locale.py b/lib/portage/util/locale.py
index b5da8d949..b6a41e765 100644
--- a/lib/portage/util/locale.py
+++ b/lib/portage/util/locale.py
@@ -17,6 +17,7 @@ import traceback
import portage
from portage.util import _unicode_decode, writemsg_level
from portage.util._ctypes import find_library, LoadLibrary
+from portage.util.futures import asyncio
locale_categories = (
@@ -121,7 +122,10 @@ def check_locale(silent=False, env=None):
warning and returns False if it is not. Returns None if the check
can not be executed due to platform limitations.
"""
+ return asyncio.run(async_check_locale(silent=silent, env=env))
+
+async def async_check_locale(silent=False, env=None):
if env is not None:
for v in ("LC_ALL", "LC_CTYPE", "LANG"):
if v in env:
@@ -135,20 +139,17 @@ def check_locale(silent=False, env=None):
except KeyError:
pass
- # TODO: Make async version of check_locale and call it from
- # EbuildPhase instead of config.environ(), since it's bad to
- # synchronously wait for the process in the main event loop
- # thread where config.environ() tends to be called.
proc = multiprocessing.Process(
target=_set_and_check_locale,
args=(silent, env, None if env is None else portage._native_string(mylocale)),
)
proc.start()
- proc.join()
+ proc = portage.process.MultiprocessingProcess(proc)
+ await proc.wait()
pyret = None
- if proc.exitcode >= 0:
- ret = proc.exitcode
+ if proc.returncode >= 0:
+ ret = proc.returncode
if ret != 2:
pyret = ret == 0
@@ -157,13 +158,22 @@ def check_locale(silent=False, env=None):
return pyret
+async_check_locale.__doc__ = check_locale.__doc__
+async_check_locale.__doc__ += """
+ This function is a coroutine.
+"""
+
+
def split_LC_ALL(env):
"""
Replace LC_ALL with split-up LC_* variables if it is defined.
Works on the passed environment (or settings instance).
"""
lc_all = env.get("LC_ALL")
- if lc_all is not None:
+ if lc_all:
for c in locale_categories:
env[c] = lc_all
- del env["LC_ALL"]
+ # Set empty so that config.reset() can restore LC_ALL state,
+ # since del can permanently delete variables which are not
+ # stored in the config's backupenv.
+ env["LC_ALL"] = ""