blob: 552a54fdb047a907af921ca24c798c6308f77574 [file] [log] [blame]
Andrew de los Reyes35a7af12010-03-10 16:33:26 -08001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/cycle_breaker.h"
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -07006#include <inttypes.h>
Andrew de los Reyes35a7af12010-03-10 16:33:26 -08007#include <set>
8#include <utility>
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -07009#include "base/string_util.h"
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080010#include "update_engine/graph_utils.h"
11#include "update_engine/tarjan.h"
12#include "update_engine/utils.h"
13
14using std::make_pair;
15using std::set;
16using std::vector;
17
18namespace chromeos_update_engine {
19
20// This is the outer function from the original paper.
21void CycleBreaker::BreakCycles(const Graph& graph, set<Edge>* out_cut_edges) {
22 cut_edges_.clear();
23
24 // Make a copy, which we will modify by removing edges. Thus, in each
25 // iteration subgraph_ is the current subgraph or the original with
26 // vertices we desire. This variable was "A_K" in the original paper.
27 subgraph_ = graph;
28
29 // The paper calls for the "adjacency structure (i.e., graph) of
30 // strong (-ly connected) component K with least vertex in subgraph
31 // induced by {s, s + 1, ..., n}".
32 // We arbitrarily order each vertex by its index in the graph. Thus,
33 // each iteration, we are looking at the subgraph {s, s + 1, ..., n}
34 // and looking for the strongly connected component with vertex s.
35
36 TarjanAlgorithm tarjan;
37
38 for (Graph::size_type i = 0; i < subgraph_.size(); i++) {
39 if (i > 0) {
40 // Erase node (i - 1) from subgraph_. First, erase what it points to
41 subgraph_[i - 1].out_edges.clear();
42 // Now, erase any pointers to node (i - 1)
43 for (Graph::size_type j = i; j < subgraph_.size(); j++) {
44 subgraph_[j].out_edges.erase(i - 1);
45 }
46 }
47
48 // Calculate SCC (strongly connected component) with vertex i.
49 vector<Vertex::Index> component_indexes;
50 tarjan.Execute(i, &subgraph_, &component_indexes);
51
52 // Set subgraph edges for the components in the SCC.
53 for (vector<Vertex::Index>::iterator it = component_indexes.begin();
54 it != component_indexes.end(); ++it) {
55 subgraph_[*it].subgraph_edges.clear();
56 for (vector<Vertex::Index>::iterator jt = component_indexes.begin();
57 jt != component_indexes.end(); ++jt) {
58 // If there's a link from *it -> *jt in the graph,
59 // add a subgraph_ edge
60 if (utils::MapContainsKey(subgraph_[*it].out_edges, *jt))
61 subgraph_[*it].subgraph_edges.insert(*jt);
62 }
63 }
64
65 current_vertex_ = i;
66 blocked_.clear();
67 blocked_.resize(subgraph_.size());
68 blocked_graph_.clear();
69 blocked_graph_.resize(subgraph_.size());
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070070 Circuit(current_vertex_, 0);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080071 }
72
73 out_cut_edges->swap(cut_edges_);
74 DCHECK(stack_.empty());
75}
76
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070077static const size_t kMaxEdgesToConsider = 2;
78
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080079void CycleBreaker::HandleCircuit() {
80 stack_.push_back(current_vertex_);
81 CHECK_GE(stack_.size(), 2);
82 Edge min_edge = make_pair(stack_[0], stack_[1]);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070083 uint64_t min_edge_weight = kuint64max;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070084 size_t edges_considered = 0;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080085 for (vector<Vertex::Index>::const_iterator it = stack_.begin();
86 it != (stack_.end() - 1); ++it) {
87 Edge edge = make_pair(*it, *(it + 1));
88 if (cut_edges_.find(edge) != cut_edges_.end()) {
89 stack_.pop_back();
90 return;
91 }
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070092 uint64_t edge_weight = graph_utils::EdgeWeight(subgraph_, edge);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080093 if (edge_weight < min_edge_weight) {
94 min_edge_weight = edge_weight;
95 min_edge = edge;
96 }
Andrew de los Reyes45109892010-07-26 13:49:31 -070097 edges_considered++;
98 if (edges_considered == kMaxEdgesToConsider)
99 break;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800100 }
101 cut_edges_.insert(min_edge);
102 stack_.pop_back();
103}
104
105void CycleBreaker::Unblock(Vertex::Index u) {
106 blocked_[u] = false;
107
108 for (Vertex::EdgeMap::iterator it = blocked_graph_[u].out_edges.begin();
109 it != blocked_graph_[u].out_edges.end(); ) {
110 Vertex::Index w = it->first;
111 blocked_graph_[u].out_edges.erase(it++);
112 if (blocked_[w])
113 Unblock(w);
114 }
115}
116
Andrew de los Reyes45109892010-07-26 13:49:31 -0700117bool CycleBreaker::StackContainsCutEdge() const {
118 for (std::vector<Vertex::Index>::const_iterator it = ++stack_.begin(),
119 e = stack_.end(); it != e; ++it) {
120 Edge edge = make_pair(*(it - 1), *it);
121 if (utils::SetContainsKey(cut_edges_, edge)) {
122 return true;
123 }
124 }
125 return false;
126}
127
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700128bool CycleBreaker::Circuit(Vertex::Index vertex, Vertex::Index depth) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800129 // "vertex" was "v" in the original paper.
130 bool found = false; // Was "f" in the original paper.
131 stack_.push_back(vertex);
132 blocked_[vertex] = true;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700133 {
134 static int counter = 0;
135 counter++;
136 if (counter == 10000) {
137 counter = 0;
138 std::string stack_str;
139 for (vector<Vertex::Index>::const_iterator it = stack_.begin();
140 it != stack_.end(); ++it) {
141 stack_str += StringPrintf("%lu -> ",
142 static_cast<long unsigned int>(*it));
143 }
144 LOG(INFO) << "stack: " << stack_str;
145 }
146 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800147
148 for (Vertex::SubgraphEdgeMap::iterator w =
149 subgraph_[vertex].subgraph_edges.begin();
150 w != subgraph_[vertex].subgraph_edges.end(); ++w) {
151 if (*w == current_vertex_) {
152 // The original paper called for printing stack_ followed by
153 // current_vertex_ here, which is a cycle. Instead, we call
154 // HandleCircuit() to break it.
155 HandleCircuit();
156 found = true;
157 } else if (!blocked_[*w]) {
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700158 if (Circuit(*w, depth + 1)) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800159 found = true;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700160 if ((depth > kMaxEdgesToConsider) || StackContainsCutEdge())
Andrew de los Reyes45109892010-07-26 13:49:31 -0700161 break;
162 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800163 }
164 }
165
166 if (found) {
167 Unblock(vertex);
168 } else {
169 for (Vertex::SubgraphEdgeMap::iterator w =
170 subgraph_[vertex].subgraph_edges.begin();
171 w != subgraph_[vertex].subgraph_edges.end(); ++w) {
Andrew de los Reyes8a51e5c2010-07-26 13:40:03 -0700172 if (blocked_graph_[*w].out_edges.find(vertex) ==
173 blocked_graph_[*w].out_edges.end()) {
174 blocked_graph_[*w].out_edges.insert(make_pair(vertex,
175 EdgeProperties()));
176 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800177 }
178 }
179 CHECK_EQ(vertex, stack_.back());
180 stack_.pop_back();
181 return found;
182}
183
184} // namespace chromeos_update_engine