MergeProcess: lock vdb earlier when appropriate
authorZac Medico <zmedico@gentoo.org>
Tue, 10 May 2011 19:52:36 +0000 (12:52 -0700)
committerZac Medico <zmedico@gentoo.org>
Tue, 10 May 2011 19:52:36 +0000 (12:52 -0700)
pym/portage/dbapi/_MergeProcess.py

index 05f45d51ade9a20d82264cc40d1de4f7bb239469..62f5bec96bd9840185165fe6ea7ef395ead323ae 100644 (file)
@@ -27,7 +27,7 @@ class MergeProcess(SpawnProcess):
        __slots__ = ('dblink', 'mycat', 'mypkg', 'settings', 'treetype',
                'vartree', 'scheduler', 'blockers', 'pkgloc', 'infloc', 'myebuild',
                'mydbapi', 'prev_mtimes', '_elog_reader_fd', '_elog_reg_id',
-               '_buf', '_elog_keys')
+               '_buf', '_elog_keys', '_locked_vdb')
 
        def _start(self):
                # Portage should always call setcpv prior to this
@@ -48,6 +48,25 @@ class MergeProcess(SpawnProcess):
                self._handle_self_reinstall()
                super(MergeProcess, self)._start()
 
+       def _lock_vdb(self):
+               """
+               Lock the vdb if FEATURES=parallel-install is NOT enabled,
+               otherwise do nothing. This is implemented with
+               vardbapi.lock(), which supports reentrance by the
+               subprocess that we spawn.
+               """
+               if "parallel-install" not in self.settings.features:
+                       self.vartree.dbapi.lock()
+                       self._locked_vdb = True
+
+       def _unlock_vdb(self):
+               """
+               Unlock the vdb if we hold a lock, otherwise do nothing.
+               """
+               if self._locked_vdb:
+                       self.vartree.dbapi.unlock()
+                       self._locked_vdb = False
+
        def _handle_self_reinstall(self):
                """
                If portage is reinstalling itself, create temporary
@@ -143,6 +162,14 @@ class MergeProcess(SpawnProcess):
                fd_pipes[elog_writer_fd] = elog_writer_fd
                self._elog_reg_id = self.scheduler.register(elog_reader_fd,
                        self._registered_events, self._elog_output_handler)
+
+               # If a concurrent emerge process tries to install a package
+               # in the same SLOT as this one at the same time, there is an
+               # extremely unlikely chance that the COUNTER values will not be
+               # ordered correctly unless we lock the vdb here.
+               # FEATURES=parallel-install skips this lock in order to
+               # improve performance, and the risk is practically negligible.
+               self._lock_vdb()
                counter = self.vartree.dbapi.counter_tick()
 
                pid = os.fork()
@@ -211,6 +238,7 @@ class MergeProcess(SpawnProcess):
                """
                Unregister from the scheduler and close open files.
                """
+               self._unlock_vdb()
                if self._elog_reg_id is not None:
                        self.scheduler.unregister(self._elog_reg_id)
                        self._elog_reg_id = None