From: Zac Medico Date: Sun, 27 Apr 2008 00:25:51 +0000 (-0000) Subject: Use digraphs to clean up blocker reference counting in the depgraph. X-Git-Tag: v2.1.5~140 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=f0d3958c46eae8daece64e353e65b1eff9f84df4;p=portage.git Use digraphs to clean up blocker reference counting in the depgraph. (trunk r9981) svn path=/main/branches/2.1.2/; revision=9987 --- diff --git a/bin/emerge b/bin/emerge index eec45e34b..27b8ddc2e 100755 --- a/bin/emerge +++ b/bin/emerge @@ -1091,6 +1091,8 @@ class BlockerDepPriority(DepPriority): def __int__(self): return 0 +BlockerDepPriority.instance = BlockerDepPriority() + class UnmergeDepPriority(AbstractDepPriority): __slots__ = () """ @@ -1862,9 +1864,12 @@ class depgraph(object): self._atom_arg_map = {} # contains all nodes pulled in by self._set_atoms self._set_nodes = set() - self.blocker_digraph = digraph() - self.blocker_parents = {} - self._unresolved_blocker_parents = {} + # Contains only Blocker -> Uninstall edges + self._blocker_uninstalls = digraph() + # Contains only Package -> Blocker edges + self._blocker_parents = digraph() + # Contains only unsolvable Package -> Blocker edges + self._unsolvable_blockers = digraph() self._slot_collision_info = set() # Slot collision nodes are not allowed to block other packages since # blocker validation is only able to account for one package per slot. @@ -2028,9 +2033,8 @@ class depgraph(object): return 1 # The blocker applies to the root where # the parent is or will be installed. - self.blocker_parents.setdefault( - Blocker(atom=dep.atom, root=dep.parent.root), set()).add( - dep.parent) + blocker = Blocker(atom=dep.atom, root=dep.parent.root) + self._blocker_parents.add(blocker, dep.parent) return 1 dep_pkg, existing_node = self._select_package(dep.root, dep.atom, onlydeps=dep.onlydeps) @@ -3276,17 +3280,12 @@ class depgraph(object): blocker_cache.BlockerData(counter, blocker_atoms) if blocker_atoms: for myatom in blocker_atoms: - blocker = ("blocks", myroot, myatom[1:]) - myparents = \ - self.blocker_parents.get(blocker, None) - if not myparents: - myparents = set() - self.blocker_parents[blocker] = myparents - myparents.add(node) + blocker = Blocker(atom=myatom[1:], root=myroot) + self._blocker_parents.add(blocker, node) blocker_cache.flush() del blocker_cache - for blocker in self.blocker_parents.keys(): + for blocker in self._blocker_parents.leaf_nodes(): self.spinner.update() mytype, myroot, mydep = blocker initial_db = self.trees[myroot]["vartree"].dbapi @@ -3294,7 +3293,12 @@ class depgraph(object): blocked_initial = initial_db.match(mydep) blocked_final = final_db.match(mydep) if not blocked_initial and not blocked_final: - del self.blocker_parents[blocker] + parent_pkgs = self._blocker_parents.parent_nodes(blocker) + self._blocker_parents.remove(blocker) + # Discard any parents that don't have any more blockers. + for pkg in parent_pkgs: + if not self._blocker_parents.child_nodes(pkg): + self._blocker_parents.remove(pkg) continue blocked_slots_initial = {} blocked_slots_final = {} @@ -3306,7 +3310,7 @@ class depgraph(object): blocked_slots_final[cpv] = \ "%s:%s" % (portage.dep_getkey(cpv), final_db.aux_get(cpv, ["SLOT"])[0]) - for parent in list(self.blocker_parents[blocker]): + for parent in self._blocker_parents.parent_nodes(blocker): ptype, proot, pcpv, pstatus = parent pdbapi = self.trees[proot][self.pkg_tree_map[ptype]].dbapi pslot = pdbapi.aux_get(pcpv, ["SLOT"])[0] @@ -3384,18 +3388,19 @@ class depgraph(object): self._pkg_cache[uninst_task] = uninst_task # Enforce correct merge order with a hard dep. self.digraph.addnode(uninst_task, inst_task, - priority=BlockerDepPriority()) + priority=BlockerDepPriority.instance) # Count references to this blocker so that it can be # invalidated after nodes referencing it have been # merged. - self.blocker_digraph.addnode(uninst_task, blocker) + self._blocker_uninstalls.addnode(uninst_task, blocker) if not unresolved_blocks and not depends_on_order: - self.blocker_parents[blocker].remove(parent) + self._blocker_parents.remove_edge(blocker, parent) + if not self._blocker_parents.parent_nodes(blocker): + self._blocker_parents.remove(blocker) + if not self._blocker_parents.child_nodes(parent): + self._blocker_parents.remove(parent) if unresolved_blocks: - self._unresolved_blocker_parents.setdefault( - blocker, set()).add(parent) - if not self.blocker_parents[blocker]: - del self.blocker_parents[blocker] + self._unsolvable_blockers.add(blocker, parent) return True @@ -3471,7 +3476,7 @@ class depgraph(object): elif n1_n2_medium: return 1 return -1 - myblockers = self.blocker_digraph.copy() + myblocker_uninstalls = self._blocker_uninstalls.copy() retlist=[] # Contains any Uninstall tasks that have been ignored # in order to avoid the circular deps code path. These @@ -3480,9 +3485,7 @@ class depgraph(object): ignored_uninstall_tasks = set() have_uninstall_task = False complete = "complete" in self.myparams - myblocker_parents = self.blocker_parents.copy() - for k, v in myblocker_parents.iteritems(): - myblocker_parents[k] = v.copy() + myblocker_parents = self._blocker_parents.copy() asap_nodes = [] portage_node = None def get_nodes(**kwargs): @@ -3655,13 +3658,13 @@ class depgraph(object): selected_nodes = list(selected_nodes) selected_nodes.sort(cmp_circular_bias) - if not selected_nodes and not myblockers.is_empty(): + if not selected_nodes and not myblocker_uninstalls.is_empty(): # An Uninstall task needs to be executed in order to # avoid conflict if possible. min_parent_deps = None uninst_task = None - for task in myblockers.leaf_nodes(): + for task in myblocker_uninstalls.leaf_nodes(): # Do some sanity checks so that system or world packages # don't get uninstalled inappropriately here (only really # necessary when --complete-graph has not been enabled). @@ -3746,7 +3749,7 @@ class depgraph(object): # to avoid the circular deps code path, but the # blocker will still be counted as an unresolved # conflict. - for node in myblockers.leaf_nodes(): + for node in myblocker_uninstalls.leaf_nodes(): try: mygraph.remove(node) except KeyError: @@ -3829,24 +3832,23 @@ class depgraph(object): pass if uninst_task is not None and \ uninst_task not in ignored_uninstall_tasks and \ - myblockers.contains(uninst_task): - myblockers.remove(uninst_task) - for blocker in myblockers.root_nodes(): - if myblockers.child_nodes(blocker): - continue - myblockers.remove(blocker) - unresolved = \ - self._unresolved_blocker_parents.get(blocker) - if unresolved: - myblocker_parents[blocker] = unresolved - else: - del myblocker_parents[blocker] + myblocker_uninstalls.contains(uninst_task): + blocker_nodes = myblocker_uninstalls.parent_nodes(uninst_task) + myblocker_uninstalls.remove(uninst_task) + # Discard any blockers that this Uninstall solves. + for blocker in blocker_nodes: + if not myblocker_uninstalls.child_nodes(blocker): + myblocker_uninstalls.remove(blocker) if node[-1] != "nomerge": retlist.append(list(node)) mygraph.remove(node) - for blocker in myblocker_parents: + unsolvable_blockers = set(self._unsolvable_blockers.leaf_nodes()) + for node in myblocker_uninstalls.root_nodes(): + unsolvable_blockers.add(node) + + for blocker in unsolvable_blockers: retlist.append(list(blocker)) # If any Uninstall tasks need to be executed in order @@ -3856,7 +3858,7 @@ class depgraph(object): # are properly identified and blocked from execution). if have_uninstall_task and \ not complete and \ - not myblocker_parents: + not unsolvable_blockers: self.myparams.add("complete") raise self._serialize_tasks_retry("") @@ -4064,7 +4066,7 @@ class depgraph(object): addl = addl + " " + red(resolved) else: addl = "[blocks " + addl + "] " + red(resolved) - block_parents = self.blocker_parents[tuple(x)] + block_parents = self._blocker_parents.parent_nodes(tuple(x)) block_parents = set([pnode[2] for pnode in block_parents]) block_parents = ", ".join(block_parents) if resolved!=x[2]: diff --git a/pym/portage.py b/pym/portage.py index 430c7872c..f11e8877e 100644 --- a/pym/portage.py +++ b/pym/portage.py @@ -391,6 +391,28 @@ class digraph: del self.nodes[node] self.order.remove(node) + def remove_edge(self, child, parent): + """ + Remove edge in the direction from child to parent. Note that it is + possible for a remaining edge to exist in the opposite direction. + Any endpoint vertices that become isolated will remain in the graph. + """ + + # Nothing should be modified when a KeyError is raised. + for k in parent, child: + if k not in self.nodes: + raise KeyError(k) + + # Make sure the edge exists. + if child not in self.nodes[parent][0]: + raise KeyError(child) + if parent not in self.nodes[child][1]: + raise KeyError(parent) + + # Remove the edge. + del self.nodes[child][1][parent] + del self.nodes[parent][0][child] + def contains(self, node): """Checks if the digraph contains mynode""" return node in self.nodes