commit:     69fbd8f9f76df32d9bc72510e5d9348eb8f059bc
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Tue Feb 13 04:04:53 2024 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Feb 21 15:27:30 2024 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=69fbd8f9

EbuildMetadataPhase: Add deallocate_config future

Use a deallocate_config future to release self.settings when
it is no longer needed. It's necessary to manage concurrency
since commit c95fc64abf96 because mutation of self.settings
is no longer limited to the EbuildMetadataPhase _start method,
where exclusive access was guaranteed within the main thread.

Add support to the isAlive() method to detect when the
EbuildMetadataPhase has started but the pid is not yet
available (due to async_check_locale usage from commit
c95fc64abf96). This can be used to check if an
EbuildMetadataPhase instance has been successfully started
so that it can be relied upon to set the result of the
deallocate_config future.

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

 lib/_emerge/EbuildMetadataPhase.py | 34 ++++++++++++++++++++++++++++++++++
 lib/_emerge/SubProcess.py          |  5 ++++-
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/lib/_emerge/EbuildMetadataPhase.py 
b/lib/_emerge/EbuildMetadataPhase.py
index f4f685e81c..249086f8af 100644
--- a/lib/_emerge/EbuildMetadataPhase.py
+++ b/lib/_emerge/EbuildMetadataPhase.py
@@ -14,6 +14,7 @@ from portage import os
 from portage import _encodings
 from portage import _unicode_decode
 from portage import _unicode_encode
+from portage.util.futures import asyncio
 
 import fcntl
 
@@ -33,6 +34,7 @@ class EbuildMetadataPhase(SubProcess):
         "portdb",
         "repo_path",
         "settings",
+        "deallocate_config",
         "write_auxdb",
     ) + (
         "_eapi",
@@ -127,6 +129,15 @@ class EbuildMetadataPhase(SubProcess):
             returnproc=True,
         )
         settings.pop("PORTAGE_PIPE_FD", None)
+        # At this point we can return settings to the caller
+        # since we never use it for anything more than an
+        # eapi_invalid call after this, and eapi_invalid is
+        # insensitive to concurrent modifications.
+        if (
+            self.deallocate_config is not None
+            and not self.deallocate_config.cancelled()
+        ):
+            self.deallocate_config.set_result(settings)
 
         os.close(slave_fd)
         null_input.close()
@@ -139,6 +150,29 @@ class EbuildMetadataPhase(SubProcess):
 
         self._proc = retval
 
+        asyncio.ensure_future(
+            self._async_start(), loop=self.scheduler
+        ).add_done_callback(self._async_start_done)
+
+    async def _async_start(self):
+        # Call async check_locale here for bug 923841, but code
+        # also needs to migrate from _start to here, including
+        # the self.deallocate_config set_result call.
+        pass
+
+    def _async_start_done(self, future):
+        future.cancelled() or future.result()
+        if not self._was_cancelled() and future.cancelled():
+            self.cancel()
+            self._was_cancelled()
+
+        if self.deallocate_config is not None and not 
self.deallocate_config.done():
+            self.deallocate_config.set_result(self.settings)
+
+        if self.returncode is not None:
+            self._unregister()
+            self.wait()
+
     def _output_handler(self):
         while True:
             buf = self._read_buf(self._files.ebuild)

diff --git a/lib/_emerge/SubProcess.py b/lib/_emerge/SubProcess.py
index 029bbc3f44..057e0adc24 100644
--- a/lib/_emerge/SubProcess.py
+++ b/lib/_emerge/SubProcess.py
@@ -18,9 +18,12 @@ class SubProcess(AbstractPollTask):
     # we've sent a kill signal to our subprocess.
     _cancel_timeout = 1  # seconds
 
+    def isAlive(self):
+        return (self._registered or self.pid is not None) and self.returncode 
is None
+
     @property
     def pid(self):
-        return self._proc.pid
+        return None if self._proc is None else self._proc.pid
 
     def _poll(self):
         # Simply rely on _async_waitpid_cb to set the returncode.

Reply via email to