commit:     bb2484afc217058267b5ba5d2d792bc1c46203b7
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Mon Apr 23 02:27:53 2018 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Mon Apr 23 02:38:53 2018 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=bb2484af

Scheduler: fix create_world_atom event loop recursion (bug 653848)

Generate world atoms while the event loop is not running, since
otherwise portdbapi match calls in the create_world_atom function
could trigger event loop recursion.

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

 pym/_emerge/Scheduler.py         | 19 ++++++++++++++-----
 pym/_emerge/create_world_atom.py |  6 ++++--
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py
index 71fe3e07d..6778708bb 100644
--- a/pym/_emerge/Scheduler.py
+++ b/pym/_emerge/Scheduler.py
@@ -448,6 +448,7 @@ class Scheduler(PollScheduler):
                        self._pkg_cache = {}
                        self._digraph = None
                        self._mergelist = []
+                       self._world_atoms = None
                        self._deep_system_deps.clear()
                        return
 
@@ -456,6 +457,18 @@ class Scheduler(PollScheduler):
                self._digraph = graph_config.graph
                self._mergelist = graph_config.mergelist
 
+               # Generate world atoms while the event loop is not running,
+               # since otherwise portdbapi match calls in the create_world_atom
+               # function could trigger event loop recursion.
+               self._world_atoms = {}
+               for pkg in self._mergelist:
+                       if getattr(pkg, 'operation', None) != 'merge':
+                               continue
+                       atom = create_world_atom(pkg, self._args_set,
+                               pkg.root_config, before_install=True)
+                       if atom is not None:
+                               self._world_atoms[pkg] = atom
+
                if "--nodeps" in self.myopts or \
                        (self._max_jobs is not True and self._max_jobs < 2):
                        # save some memory
@@ -1939,11 +1952,7 @@ class Scheduler(PollScheduler):
                atom = None
 
                if pkg.operation != "uninstall":
-                       # Do this before acquiring the lock, since it queries 
the
-                       # portdbapi which can call the global event loop, 
triggering
-                       # a concurrent call to this method or something else 
that
-                       # needs an exclusive (non-reentrant) lock on the world 
file.
-                       atom = create_world_atom(pkg, args_set, root_config)
+                       atom = self._world_atoms.get(pkg)
 
                try:
 

diff --git a/pym/_emerge/create_world_atom.py b/pym/_emerge/create_world_atom.py
index 74b0fa5d7..947f8088a 100644
--- a/pym/_emerge/create_world_atom.py
+++ b/pym/_emerge/create_world_atom.py
@@ -11,7 +11,7 @@ if sys.hexversion >= 0x3000000:
 else:
        _unicode = unicode
 
-def create_world_atom(pkg, args_set, root_config):
+def create_world_atom(pkg, args_set, root_config, before_install=False):
        """Create a new atom for the world file if one does not exist.  If the
        argument atom is precise enough to identify a specific slot then a slot
        atom will be returned. Atoms that are in the system set may also be 
stored
@@ -83,11 +83,13 @@ def create_world_atom(pkg, args_set, root_config):
                # If there is no installed package matching the SLOT atom,
                # it probably changed SLOT spontaneously due to USE=multislot,
                # so just record an unslotted atom.
-               if vardb.match(slot_atom):
+               if vardb.match(slot_atom) or before_install:
                        # Now verify that the argument is precise
                        # enough to identify a specific slot.
                        matches = mydb.match(arg_atom)
                        matched_slots = set()
+                       if before_install:
+                               matched_slots.add(pkg.slot)
                        if mydb is vardb:
                                for cpv in matches:
                                        matched_slots.add(mydb._pkg_str(cpv, 
None).slot)

Reply via email to