summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2008-11-10 22:30:35 +0000
committerZac Medico <zmedico@gentoo.org>2008-11-10 22:30:35 +0000
commit709e2d897715bdd238387719c129210623285983 (patch)
tree9e27a1f04d68249e4cc818791a9b4637bf6dd941
parent36062fb6b1fe1d7e9aca6ba95b36e322d0e863a8 (diff)
downloadportage-709e2d897715bdd238387719c129210623285983.tar.gz
portage-709e2d897715bdd238387719c129210623285983.tar.bz2
portage-709e2d897715bdd238387719c129210623285983.zip
Fix --jobs parallel scheduling to ensure that temporary simultaneous
installation of conflicting packages is avoided when appropriate (especially for !!atom blockers), but allowed in specific cases that require it. This is accomplished by reversing specific uninstall edges in the digraph, while possibly leaving some edges in there original state. svn path=/main/trunk/; revision=11845
-rw-r--r--pym/_emerge/__init__.py74
1 files changed, 35 insertions, 39 deletions
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py
index 74f07ec05..4ebfbde9e 100644
--- a/pym/_emerge/__init__.py
+++ b/pym/_emerge/__init__.py
@@ -4300,6 +4300,7 @@ class depgraph(object):
# blocker validation is only able to account for one package per slot.
self._slot_collision_nodes = set()
self._serialized_tasks_cache = None
+ self._scheduler_graph = None
self._displayed_list = None
self._pprovided_args = []
self._missing_args = []
@@ -6154,7 +6155,8 @@ class depgraph(object):
while self._serialized_tasks_cache is None:
self._resolve_conflicts()
try:
- self._serialized_tasks_cache = self._serialize_tasks()
+ self._serialized_tasks_cache, self._scheduler_graph = \
+ self._serialize_tasks()
except self._serialize_tasks_retry:
pass
@@ -6163,6 +6165,25 @@ class depgraph(object):
retlist.reverse()
return retlist
+ def schedulerGraph(self):
+ """
+ The scheduler graph is identical to the normal one except that
+ uninstall edges are reversed in specific cases that require
+ conflicting packages to be temporarily installed simultaneously.
+ This is intended for use by the Scheduler in it's parallelization
+ logic. It ensures that temporary simultaneous installation of
+ conflicting packages is avoided when appropriate (especially for
+ !!atom blockers), but allowed in specific cases that require it.
+
+ Note that this method calls break_refs() which alters the state of
+ internal Package instances such that this depgraph instance should
+ not be used to perform any more calculations.
+ """
+ if self._scheduler_graph is None:
+ self.altlist()
+ self.break_refs(self._scheduler_graph.order)
+ return self._scheduler_graph
+
def break_refs(self, nodes):
"""
Take a mergelist like that returned from self.altlist() and
@@ -6189,6 +6210,7 @@ class depgraph(object):
raise self._unknown_internal_error()
def _serialize_tasks(self):
+ scheduler_graph = self.digraph.copy()
mygraph=self.digraph.copy()
# Prune "nomerge" root nodes if nothing depends on them, since
# otherwise they slow down merge order calculation. Don't remove
@@ -6608,6 +6630,10 @@ class depgraph(object):
for blocked_pkg in parent_nodes:
mygraph.add(blocked_pkg, uninst_task,
priority=BlockerDepPriority.instance)
+ scheduler_graph.remove_edge(uninst_task, blocked_pkg)
+ scheduler_graph.add(blocked_pkg, uninst_task,
+ priority=BlockerDepPriority.instance)
+
else:
# None of the Uninstall tasks are acceptable, so
# the corresponding blockers are unresolvable.
@@ -6718,14 +6744,16 @@ class depgraph(object):
not self._accept_blocker_conflicts():
self._unsatisfied_blockers_for_display = unsolvable_blockers
self._serialized_tasks_cache = retlist[:]
+ self._scheduler_graph = scheduler_graph
raise self._unknown_internal_error()
if self._slot_collision_info and \
not self._accept_blocker_conflicts():
self._serialized_tasks_cache = retlist[:]
+ self._scheduler_graph = scheduler_graph
raise self._unknown_internal_error()
- return retlist
+ return retlist, scheduler_graph
def _show_circular_deps(self, mygraph):
# No leaf nodes are available, so we have a circular
@@ -9247,35 +9275,8 @@ class Scheduler(PollScheduler):
return
self._digraph = digraph
- self._reverse_uninstall_edges()
self._prune_digraph()
- def _reverse_uninstall_edges(self):
- """
- The uninstall is performed only after blocking packages have been
- merged on top of it (similar to how a normal upgrade is performed
- by first merging the new version on top of the old version). This
- is implemented by reversing the parent -> uninstall edges in
- the graph.
- """
-
- graph = self._digraph
-
- # TODO: Invert specific edges that the depgraph has decided are
- # necessary and allowed to be inverted. Currently the below code
- # does not invert edges for uninstalls that happen as part of an
- # upgrade with in a slot (though it should).
- for node in self._mergelist:
- if not isinstance(node, Package) or \
- node.operation != "uninstall":
- continue
-
- parent_nodes = graph.parent_nodes(node)
- graph.remove(node)
- for blocked_pkg in parent_nodes:
- graph.add(blocked_pkg, node,
- priority=BlockerDepPriority.instance)
-
def _prune_digraph(self):
"""
Prune any root nodes that are irrelevant.
@@ -10268,11 +10269,8 @@ class Scheduler(PollScheduler):
mylist = mydepgraph.altlist()
mydepgraph.break_refs(mylist)
- mydepgraph.break_refs(dropped_tasks)
- mydepgraph.break_refs(mydepgraph.digraph.order)
-
self._mergelist = mylist
- self._set_digraph(mydepgraph.digraph)
+ self._set_digraph(mydepgraph.schedulerGraph())
msg_width = 75
for task in dropped_tasks:
@@ -13187,10 +13185,9 @@ def action_build(settings, trees, mtimedb,
time.sleep(3) # allow the parent to have first fetch
mymergelist = mydepgraph.altlist()
mydepgraph.break_refs(mymergelist)
- mydepgraph.break_refs(mydepgraph.digraph.order)
mergetask = Scheduler(settings, trees, mtimedb, myopts,
- spinner, mymergelist, favorites, mydepgraph.digraph)
- del mydepgraph
+ spinner, mymergelist, favorites, mydepgraph.schedulerGraph())
+ del mydepgraph, mymergelist
clear_caches(trees)
retval = mergetask.merge()
@@ -13235,10 +13232,9 @@ def action_build(settings, trees, mtimedb,
pkglist = mydepgraph.altlist()
mydepgraph.saveNomergeFavorites()
mydepgraph.break_refs(pkglist)
- mydepgraph.break_refs(mydepgraph.digraph.order)
mergetask = Scheduler(settings, trees, mtimedb, myopts,
- spinner, pkglist, favorites, mydepgraph.digraph)
- del mydepgraph
+ spinner, pkglist, favorites, mydepgraph.schedulerGraph())
+ del mydepgraph, pkglist
clear_caches(trees)
retval = mergetask.merge()