commit:     937d0156aa060bdba9095313dedbb62e0a993aea
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Jun  6 01:52:59 2018 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Jun  6 02:09:55 2018 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=937d0156

CompositeTask: handle SIGINT/TERM cancelled futures (bug 657436)

In order to avoid raising an unwanted CancelledError, make
CompositeTask check for cancelled futures before attempting
to call future.result(). The intention is only to handle
failures triggered by SIGINT/TERM, since other types of
expected failures should always be handled by catching the
exception raised from future.result().

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

 pym/_emerge/Binpkg.py                     | 10 +++++++++-
 pym/_emerge/BinpkgFetcher.py              |  8 ++++++++
 pym/_emerge/CompositeTask.py              |  1 +
 pym/_emerge/EbuildBuild.py                | 16 ++++++++++++++++
 pym/_emerge/EbuildFetcher.py              | 12 ++++++++++++
 pym/_emerge/EbuildPhase.py                |  4 ++++
 pym/_emerge/PackageUninstall.py           |  8 ++++++++
 pym/portage/_emirrordist/FetchIterator.py | 10 ++++++++++
 8 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/pym/_emerge/Binpkg.py b/pym/_emerge/Binpkg.py
index 2b67816e8..7791ec236 100644
--- a/pym/_emerge/Binpkg.py
+++ b/pym/_emerge/Binpkg.py
@@ -122,6 +122,10 @@ class Binpkg(CompositeTask):
        def _start_fetcher(self, lock_task=None):
                if lock_task is not None:
                        self._assert_current(lock_task)
+                       if lock_task.cancelled:
+                               self._default_final_exit(lock_task)
+                               return
+
                        lock_task.future.result()
                        # Initialize PORTAGE_LOG_FILE (clean_log won't work 
without it).
                        portage.prepare_build_dirs(self.settings["ROOT"], 
self.settings, 1)
@@ -411,8 +415,12 @@ class Binpkg(CompositeTask):
 
        def _unlock_builddir_exit(self, unlock_task, returncode=None):
                self._assert_current(unlock_task)
+               if unlock_task.cancelled and returncode is not None:
+                       self._default_final_exit(unlock_task)
+                       return
+
                # Normally, async_unlock should not raise an exception here.
-               unlock_task.future.result()
+               unlock_task.future.cancelled() or unlock_task.future.result()
                if returncode is not None:
                        self.returncode = returncode
                        self._async_wait()

diff --git a/pym/_emerge/BinpkgFetcher.py b/pym/_emerge/BinpkgFetcher.py
index 8e651a1c7..36d027de3 100644
--- a/pym/_emerge/BinpkgFetcher.py
+++ b/pym/_emerge/BinpkgFetcher.py
@@ -48,6 +48,10 @@ class BinpkgFetcher(CompositeTask):
 
        def _start_locked(self, fetcher, lock_task):
                self._assert_current(lock_task)
+               if lock_task.cancelled:
+                       self._default_final_exit(lock_task)
+                       return
+
                lock_task.future.result()
                self._start_task(fetcher, self._fetcher_exit)
 
@@ -65,6 +69,10 @@ class BinpkgFetcher(CompositeTask):
        def _fetcher_exit_unlocked(self, fetcher, unlock_task=None):
                if unlock_task is not None:
                        self._assert_current(unlock_task)
+                       if unlock_task.cancelled:
+                               self._default_final_exit(unlock_task)
+                               return
+
                        unlock_task.future.result()
 
                self._current_task = None

diff --git a/pym/_emerge/CompositeTask.py b/pym/_emerge/CompositeTask.py
index 4662f0cf5..1edec4a17 100644
--- a/pym/_emerge/CompositeTask.py
+++ b/pym/_emerge/CompositeTask.py
@@ -69,6 +69,7 @@ class CompositeTask(AsynchronousTask):
                self._assert_current(task)
                if task.returncode != os.EX_OK:
                        self.returncode = task.returncode
+                       self.cancelled = task.cancelled
                        self._current_task = None
                return task.returncode
 

diff --git a/pym/_emerge/EbuildBuild.py b/pym/_emerge/EbuildBuild.py
index d9f7f6da7..8d264dd1c 100644
--- a/pym/_emerge/EbuildBuild.py
+++ b/pym/_emerge/EbuildBuild.py
@@ -54,6 +54,10 @@ class EbuildBuild(CompositeTask):
 
        def _start_with_metadata(self, aux_get_task):
                self._assert_current(aux_get_task)
+               if aux_get_task.cancelled:
+                       self._default_final_exit(aux_get_task)
+                       return
+
                pkg = self.pkg
                settings = self.settings
                root_config = pkg.root_config
@@ -178,6 +182,10 @@ class EbuildBuild(CompositeTask):
 
        def _start_pre_clean(self, lock_task):
                self._assert_current(lock_task)
+               if lock_task.cancelled:
+                       self._default_final_exit(lock_task)
+                       return
+
                lock_task.future.result()
                # Cleaning needs to happen before fetch, since the build dir
                # is used for log handling.
@@ -235,6 +243,10 @@ class EbuildBuild(CompositeTask):
 
        def _start_fetch(self, fetcher, already_fetched_task):
                self._assert_current(already_fetched_task)
+               if already_fetched_task.cancelled:
+                       self._default_final_exit(already_fetched_task)
+                       return
+
                try:
                        already_fetched = already_fetched_task.future.result()
                except portage.exception.InvalidDependString as e:
@@ -342,6 +354,10 @@ class EbuildBuild(CompositeTask):
 
        def _unlock_builddir_exit(self, unlock_task, returncode=None):
                self._assert_current(unlock_task)
+               if unlock_task.cancelled:
+                       self._default_final_exit(unlock_task)
+                       return
+
                # Normally, async_unlock should not raise an exception here.
                unlock_task.future.result()
                if returncode is not None:

diff --git a/pym/_emerge/EbuildFetcher.py b/pym/_emerge/EbuildFetcher.py
index 3b30ebb59..ad5109c28 100644
--- a/pym/_emerge/EbuildFetcher.py
+++ b/pym/_emerge/EbuildFetcher.py
@@ -47,6 +47,10 @@ class EbuildFetcher(CompositeTask):
 
        def _start_fetch(self, uri_map_task):
                self._assert_current(uri_map_task)
+               if uri_map_task.cancelled:
+                       self._default_final_exit(uri_map_task)
+                       return
+
                try:
                        uri_map = uri_map_task.future.result()
                except portage.exception.InvalidDependString as e:
@@ -71,6 +75,10 @@ class EbuildFetcher(CompositeTask):
 
        def _start_with_metadata(self, aux_get_task):
                self._assert_current(aux_get_task)
+               if aux_get_task.cancelled:
+                       self._default_final_exit(aux_get_task)
+                       return
+
                self._fetcher_proc.src_uri, = aux_get_task.future.result()
                self._start_task(self._fetcher_proc, self._default_final_exit)
 
@@ -85,6 +93,10 @@ class _EbuildFetcherProcess(ForkProcess):
                result = self.scheduler.create_future()
 
                def uri_map_done(uri_map_future):
+                       if uri_map_future.cancelled():
+                               result.cancel()
+                               return
+
                        if uri_map_future.exception() is not None or 
result.cancelled():
                                if not result.cancelled():
                                        
result.set_exception(uri_map_future.exception())

diff --git a/pym/_emerge/EbuildPhase.py b/pym/_emerge/EbuildPhase.py
index d057dc45e..4104cefa7 100644
--- a/pym/_emerge/EbuildPhase.py
+++ b/pym/_emerge/EbuildPhase.py
@@ -211,6 +211,10 @@ class EbuildPhase(CompositeTask):
        def _ebuild_exit_unlocked(self, ebuild_process, unlock_task=None):
                if unlock_task is not None:
                        self._assert_current(unlock_task)
+                       if unlock_task.cancelled:
+                               self._default_final_exit(unlock_task)
+                               return
+
                        # Normally, async_unlock should not raise an exception 
here.
                        unlock_task.future.result()
 

diff --git a/pym/_emerge/PackageUninstall.py b/pym/_emerge/PackageUninstall.py
index 3fe1fb0a6..cb3413056 100644
--- a/pym/_emerge/PackageUninstall.py
+++ b/pym/_emerge/PackageUninstall.py
@@ -61,6 +61,10 @@ class PackageUninstall(CompositeTask):
 
        def _start_unmerge(self, lock_task):
                self._assert_current(lock_task)
+               if lock_task.cancelled:
+                       self._default_final_exit(lock_task)
+                       return
+
                lock_task.future.result()
                portage.prepare_build_dirs(
                        settings=self.settings, cleanup=True)
@@ -112,6 +116,10 @@ class PackageUninstall(CompositeTask):
 
        def _unlock_builddir_exit(self, unlock_task, returncode=None):
                self._assert_current(unlock_task)
+               if unlock_task.cancelled:
+                       self._default_final_exit(unlock_task)
+                       return
+
                # Normally, async_unlock should not raise an exception here.
                unlock_task.future.result()
                if returncode is not None:

diff --git a/pym/portage/_emirrordist/FetchIterator.py 
b/pym/portage/_emirrordist/FetchIterator.py
index 04d4da62b..4ad797502 100644
--- a/pym/portage/_emirrordist/FetchIterator.py
+++ b/pym/portage/_emirrordist/FetchIterator.py
@@ -135,11 +135,21 @@ def _async_fetch_tasks(config, hash_filter, repo_config, 
digests_future, cpv,
                if not gather_result.cancelled():
                        list(future.exception() for future in 
gather_result.result()
                                if not future.cancelled())
+               else:
+                       result.cancel()
 
                if result.cancelled():
                        return
 
                aux_get_result, fetch_map_result = gather_result.result()
+               if aux_get_result.cancelled() or fetch_map_result.cancelled():
+                       # Cancel result after consuming any exceptions which
+                       # are now irrelevant due to cancellation.
+                       aux_get_result.cancelled() or aux_get_result.exception()
+                       fetch_map_result.cancelled() or 
fetch_map_result.exception()
+                       result.cancel()
+                       return
+
                try:
                        restrict, = aux_get_result.result()
                except (PortageKeyError, PortageException) as e:

Reply via email to