From: Zac Medico Date: Thu, 21 Feb 2013 22:23:57 +0000 (-0800) Subject: _world_atom: avoid world set lock reentrance X-Git-Tag: v2.2.0_alpha164~14 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2c7155b8419eb1b7ba99760a962093ad19e2ce8f;p=portage.git _world_atom: avoid world set lock reentrance 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. --- diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py index 4b29c7110..5930550fa 100644 --- a/pym/_emerge/Scheduler.py +++ b/pym/_emerge/Scheduler.py @@ -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) diff --git a/pym/portage/_sets/files.py b/pym/portage/_sets/files.py index b839582df..2fb64de87 100644 --- a/pym/portage/_sets/files.py +++ b/pym/portage/_sets/files.py @@ -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