commit:     30c69adfc0ffa450ff3a4d4d176023db66171ae7
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Sat Apr 21 06:36:29 2018 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Sun Apr 22 18:30:52 2018 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=30c69adf

AbstractEbuildProcess: use async_lock (bug 614112)

Asynchronously lock the build directory. The asynchronous lock delays
creation of the pid, and it's possible for the _wait method to be called
before the pid is available. Therefore, AbstractEbuildProcess._wait()
must wait for the pid to become available before it can call the
SpawnProcess._wait() method.

Bug: https://bugs.gentoo.org/614112

 pym/_emerge/AbstractEbuildProcess.py | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/pym/_emerge/AbstractEbuildProcess.py 
b/pym/_emerge/AbstractEbuildProcess.py
index 2aa0c4a35..d481e6046 100644
--- a/pym/_emerge/AbstractEbuildProcess.py
+++ b/pym/_emerge/AbstractEbuildProcess.py
@@ -25,7 +25,7 @@ class AbstractEbuildProcess(SpawnProcess):
 
        __slots__ = ('phase', 'settings',) + \
                ('_build_dir', '_build_dir_unlock', '_ipc_daemon',
-               '_exit_command', '_exit_timeout_id')
+               '_exit_command', '_exit_timeout_id', '_start_future')
 
        _phases_without_builddir = ('clean', 'cleanrm', 'depend', 'help',)
        _phases_interactive_whitelist = ('config',)
@@ -130,15 +130,19 @@ class AbstractEbuildProcess(SpawnProcess):
                        # since we're not displaying to a terminal anyway.
                        self.settings['NOCOLOR'] = 'true'
 
+               start_ipc_daemon = False
                if self._enable_ipc_daemon:
                        self.settings.pop('PORTAGE_EBUILD_EXIT_FILE', None)
                        if self.phase not in self._phases_without_builddir:
+                               start_ipc_daemon = True
                                if 'PORTAGE_BUILDDIR_LOCKED' not in 
self.settings:
                                        self._build_dir = EbuildBuildDir(
                                                scheduler=self.scheduler, 
settings=self.settings)
-                                       self._build_dir.lock()
-                               self.settings['PORTAGE_IPC_DAEMON'] = "1"
-                               self._start_ipc_daemon()
+                                       self._start_future = 
self._build_dir.async_lock()
+                                       self._start_future.add_done_callback(
+                                               
functools.partial(self._start_post_builddir_lock,
+                                               
start_ipc_daemon=start_ipc_daemon))
+                                       return
                        else:
                                self.settings.pop('PORTAGE_IPC_DAEMON', None)
                else:
@@ -159,6 +163,19 @@ class AbstractEbuildProcess(SpawnProcess):
                        else:
                                self.settings.pop('PORTAGE_EBUILD_EXIT_FILE', 
None)
 
+               
self._start_post_builddir_lock(start_ipc_daemon=start_ipc_daemon)
+
+       def _start_post_builddir_lock(self, lock_future=None, 
start_ipc_daemon=False):
+               if lock_future is not None:
+                       if lock_future is not self._start_future:
+                               raise AssertionError('lock_future is not 
self._start_future')
+                       self._start_future = None
+                       lock_future.result()
+
+               if start_ipc_daemon:
+                       self.settings['PORTAGE_IPC_DAEMON'] = "1"
+                       self._start_ipc_daemon()
+
                if self.fd_pipes is None:
                        self.fd_pipes = {}
                null_fd = None
@@ -375,6 +392,11 @@ class AbstractEbuildProcess(SpawnProcess):
                Execution of the failsafe code will automatically become a fatal
                error at the same time as event loop recursion is disabled.
                """
+               # SpawnProcess._wait() requires the pid, so wait here for the
+               # pid to become available.
+               while self._start_future is not None:
+                       self.scheduler.run_until_complete(self._start_future)
+
                SpawnProcess._wait(self)
 
                if self._build_dir is not None:

Reply via email to