_world_atom: avoid world set lock reentrance
authorZac Medico <zmedico@gentoo.org>
Thu, 21 Feb 2013 22:23:57 +0000 (14:23 -0800)
committerZac Medico <zmedico@gentoo.org>
Thu, 21 Feb 2013 22:24:35 +0000 (14:24 -0800)
This fixes a case with FEATURE=parallel-install, where a call from
_world_atom to the global event loop could result in reentrace and
lock interference.

pym/_emerge/Scheduler.py
pym/portage/_sets/files.py

index 4b29c711071231cfc80324d0514d1f85da12b8e0..5930550fa9f93a3bbc3af2a78a22fa1e187d4709 100644 (file)
@@ -1896,11 +1896,21 @@ class Scheduler(PollScheduler):
                root_config = pkg.root_config
                world_set = root_config.sets["selected"]
                world_locked = False
-               if hasattr(world_set, "lock"):
-                       world_set.lock()
-                       world_locked = True
+               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)
 
                try:
+
+                       if hasattr(world_set, "lock"):
+                               world_set.lock()
+                               world_locked = True
+
                        if hasattr(world_set, "load"):
                                world_set.load() # maybe it's changed on disk
 
@@ -1912,8 +1922,7 @@ class Scheduler(PollScheduler):
                                        for s in pkg.root_config.setconfig.active:
                                                world_set.remove(SETPREFIX+s)
                        else:
-                               atom = create_world_atom(pkg, args_set, root_config)
-                               if atom:
+                               if atom is not None:
                                        if hasattr(world_set, "add"):
                                                self._status_msg(('Recording %s in "world" ' + \
                                                        'favorites file...') % atom)
index b839582dfbc2ac3442e7d09ef3a692a836ebc5a2..2fb64de8799d9f13ad6e81b5f4a5dfc827a0038d 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2007-2012 Gentoo Foundation
+# Copyright 2007-2013 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
@@ -296,10 +296,14 @@ class WorldSelectedSet(EditablePackageSet):
                ensure_dirs(os.path.dirname(self._filename), gid=portage_gid, mode=0o2750, mask=0o2)
 
        def lock(self):
+               if self._lock is not None:
+                       raise AssertionError("already locked")
                self._ensure_dirs()
                self._lock = lockfile(self._filename, wantnewlockfile=1)
 
        def unlock(self):
+               if self._lock is None:
+                       raise AssertionError("not locked")
                unlockfile(self._lock)
                self._lock = None