Discussion:
[gentoo-portage-dev] [PATCH 0/2] EbuildBuildDir: add async_unlock method (bug 614108)
Zac Medico
2018-04-19 17:02:42 UTC
Permalink
Bug: https://bugs.gentoo.org/614108

Zac Medico (2):
EbuildBuildDir: add async_unlock method (bug 614108)
EbuildBuild: use async_unlock (bug 614108)

pym/_emerge/EbuildBuild.py | 52 ++++++++++++++++++++++++++-----------------
pym/_emerge/EbuildBuildDir.py | 43 +++++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+), 20 deletions(-)
--
2.13.6
Zac Medico
2018-04-19 17:02:44 UTC
Permalink
This adds an _async_unlock_builddir method which accepts a
returncode parameter for cases where it should set the
returncode and notify exit listeners.

Bug: https://bugs.gentoo.org/614108
---
pym/_emerge/EbuildBuild.py | 52 ++++++++++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/pym/_emerge/EbuildBuild.py b/pym/_emerge/EbuildBuild.py
index 48f470483..833996c37 100644
--- a/pym/_emerge/EbuildBuild.py
+++ b/pym/_emerge/EbuildBuild.py
@@ -3,6 +3,7 @@

from __future__ import unicode_literals

+import functools
import io

import _emerge.emergelog
@@ -23,6 +24,8 @@ from portage import _encodings, _unicode_decode, _unicode_encode, os
from portage.package.ebuild.digestcheck import digestcheck
from portage.package.ebuild.doebuild import _check_temp_dir
from portage.package.ebuild._spawn_nofetch import SpawnNofetchWithoutBuilddir
+from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
+

class EbuildBuild(CompositeTask):

@@ -185,8 +188,7 @@ class EbuildBuild(CompositeTask):

def _pre_clean_exit(self, pre_clean_phase):
if self._default_exit(pre_clean_phase) != os.EX_OK:
- self._unlock_builddir()
- self.wait()
+ self._async_unlock_builddir(returncode=self.returncode)
return

# for log handling
@@ -209,10 +211,7 @@ class EbuildBuild(CompositeTask):
msg_lines.append(msg)
fetcher._eerror(msg_lines)
portage.elog.elog_process(self.pkg.cpv, self.settings)
- self.returncode = 1
- self._current_task = None
- self._unlock_builddir()
- self.wait()
+ self._async_unlock_builddir(returncode=1)
return

if already_fetched:
@@ -283,8 +282,7 @@ class EbuildBuild(CompositeTask):

if 'fetch' not in self.pkg.restrict and \
'nofetch' not in self.pkg.defined_phases:
- self._unlock_builddir()
- self.wait()
+ self._async_unlock_builddir(returncode=self.returncode)
return

self.returncode = None
@@ -294,18 +292,32 @@ class EbuildBuild(CompositeTask):

def _nofetch_exit(self, nofetch_phase):
self._final_exit(nofetch_phase)
- self._unlock_builddir()
- self.returncode = 1
- self.wait()
+ self._async_unlock_builddir(returncode=1)

- def _unlock_builddir(self):
+ def _async_unlock_builddir(self, returncode=None):
+ """
+ Release the lock asynchronously, and if a returncode parameter
+ is given then set self.returncode and notify exit listeners.
+ """
+ if returncode is not None:
+ # The returncode will be set after unlock is complete.
+ self.returncode = None
portage.elog.elog_process(self.pkg.cpv, self.settings)
- self._build_dir.unlock()
+ self._start_task(
+ AsyncTaskFuture(future=self._build_dir.async_unlock()),
+ functools.partial(self._unlock_builddir_exit, returncode=returncode))
+
+ def _unlock_builddir_exit(self, unlock_task, returncode=None):
+ self._assert_current(unlock_task)
+ # Normally, async_unlock should not raise an exception here.
+ unlock_task.future.result()
+ if returncode is not None:
+ self.returncode = returncode
+ self.wait()

def _build_exit(self, build):
if self._default_exit(build) != os.EX_OK:
- self._unlock_builddir()
- self.wait()
+ self._async_unlock_builddir(returncode=self.returncode)
return

buildpkg = self._buildpkg
@@ -370,8 +382,7 @@ class EbuildBuild(CompositeTask):
"""

if self._default_exit(packager) != os.EX_OK:
- self._unlock_builddir()
- self.wait()
+ self._async_unlock_builddir(returncode=self.returncode)
return

if self.opts.buildpkgonly:
@@ -425,8 +436,9 @@ class EbuildBuild(CompositeTask):
def _clean_exit(self, clean_phase):
if self._final_exit(clean_phase) != os.EX_OK or \
self.opts.buildpkgonly:
- self._unlock_builddir()
- self.wait()
+ self._async_unlock_builddir(returncode=self.returncode)
+ else:
+ self.wait()

def create_install_task(self):
"""
@@ -461,4 +473,4 @@ class EbuildBuild(CompositeTask):
return task

def _install_exit(self, task):
- self._unlock_builddir()
+ self._async_unlock_builddir()
--
2.13.6
Zac Medico
2018-04-19 17:02:43 UTC
Permalink
This calls the existing AsynchronousLock async_unlock method
for the build directory lock, and also handles removal of the
category directory (with async lock/unlock).

Bug: https://bugs.gentoo.org/614108
---
pym/_emerge/EbuildBuildDir.py | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)

diff --git a/pym/_emerge/EbuildBuildDir.py b/pym/_emerge/EbuildBuildDir.py
index 58905c2f6..da7128689 100644
--- a/pym/_emerge/EbuildBuildDir.py
+++ b/pym/_emerge/EbuildBuildDir.py
@@ -88,6 +88,9 @@ class EbuildBuildDir(SlotObject):
if self._lock_obj is None:
return

+ # Keep this legacy implementation until all consumers have migrated
+ # to async_unlock, since run_until_complete(self.async_unlock())
+ # would add unwanted event loop recursion here.
self._lock_obj.unlock()
self._lock_obj = None
self.locked = False
@@ -102,6 +105,46 @@ class EbuildBuildDir(SlotObject):
finally:
catdir_lock.unlock()

+ def async_unlock(self):
+ """
+ Release the lock asynchronously. Release notification is available
+ via the add_done_callback method of the returned Future instance.
+
+ @returns: Future, result is None
+ """
+ result = self.scheduler.create_future()
+
+ def builddir_unlocked(future):
+ if future.exception() is not None:
+ result.set_exception(future.exception())
+ else:
+ self._lock_obj = None
+ self.locked = False
+ self.settings.pop('PORTAGE_BUILDDIR_LOCKED', None)
+ catdir_lock = AsynchronousLock(
+ path=self._catdir, scheduler=self.scheduler)
+ catdir_lock.start()
+ catdir_lock.addExitListener(catdir_locked)
+
+ def catdir_locked(catdir_lock):
+ if catdir_lock.wait() != os.EX_OK:
+ result.set_result(None)
+ else:
+ try:
+ os.rmdir(self._catdir)
+ except OSError:
+ pass
+ catdir_lock.async_unlock().add_done_callback(catdir_unlocked)
+
+ def catdir_unlocked(future):
+ if future.exception() is None:
+ result.set_result(None)
+ else:
+ result.set_exception(future.exception())
+
+ self._lock_obj.async_unlock().add_done_callback(builddir_unlocked)
+ return result
+
class AlreadyLocked(portage.exception.PortageException):
pass
--
2.13.6
Loading...