blob: f1a2728555c1c68260076e823df3906ee05a5df8 [file] [log] [blame]
Mike Frysinger8155d082012-04-06 15:23:18 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Andrew de los Reyes35a7af12010-03-10 16:33:26 -08002// 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"
Mike Frysinger8155d082012-04-06 15:23:18 -040010#include <base/stringprintf.h>
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080011#include "update_engine/graph_utils.h"
12#include "update_engine/tarjan.h"
13#include "update_engine/utils.h"
14
15using std::make_pair;
16using std::set;
17using std::vector;
18
19namespace chromeos_update_engine {
20
21// This is the outer function from the original paper.
22void CycleBreaker::BreakCycles(const Graph& graph, set<Edge>* out_cut_edges) {
23 cut_edges_.clear();
24
25 // Make a copy, which we will modify by removing edges. Thus, in each
26 // iteration subgraph_ is the current subgraph or the original with
27 // vertices we desire. This variable was "A_K" in the original paper.
28 subgraph_ = graph;
29
30 // The paper calls for the "adjacency structure (i.e., graph) of
31 // strong (-ly connected) component K with least vertex in subgraph
32 // induced by {s, s + 1, ..., n}".
33 // We arbitrarily order each vertex by its index in the graph. Thus,
34 // each iteration, we are looking at the subgraph {s, s + 1, ..., n}
35 // and looking for the strongly connected component with vertex s.
36
37 TarjanAlgorithm tarjan;
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070038 skipped_ops_ = 0;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080039
40 for (Graph::size_type i = 0; i < subgraph_.size(); i++) {
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070041 DeltaArchiveManifest_InstallOperation_Type op_type = graph[i].op.type();
42 if (op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
43 op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
44 skipped_ops_++;
45 continue;
46 }
47
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080048 if (i > 0) {
49 // Erase node (i - 1) from subgraph_. First, erase what it points to
50 subgraph_[i - 1].out_edges.clear();
51 // Now, erase any pointers to node (i - 1)
52 for (Graph::size_type j = i; j < subgraph_.size(); j++) {
53 subgraph_[j].out_edges.erase(i - 1);
54 }
55 }
56
57 // Calculate SCC (strongly connected component) with vertex i.
58 vector<Vertex::Index> component_indexes;
59 tarjan.Execute(i, &subgraph_, &component_indexes);
60
61 // Set subgraph edges for the components in the SCC.
62 for (vector<Vertex::Index>::iterator it = component_indexes.begin();
63 it != component_indexes.end(); ++it) {
64 subgraph_[*it].subgraph_edges.clear();
65 for (vector<Vertex::Index>::iterator jt = component_indexes.begin();
66 jt != component_indexes.end(); ++jt) {
67 // If there's a link from *it -> *jt in the graph,
68 // add a subgraph_ edge
69 if (utils::MapContainsKey(subgraph_[*it].out_edges, *jt))
70 subgraph_[*it].subgraph_edges.insert(*jt);
71 }
72 }
73
74 current_vertex_ = i;
75 blocked_.clear();
76 blocked_.resize(subgraph_.size());
77 blocked_graph_.clear();
78 blocked_graph_.resize(subgraph_.size());
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070079 Circuit(current_vertex_, 0);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080080 }
81
82 out_cut_edges->swap(cut_edges_);
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070083 LOG(INFO) << "Cycle breaker skipped " << skipped_ops_ << " ops.";
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080084 DCHECK(stack_.empty());
85}
86
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070087static const size_t kMaxEdgesToConsider = 2;
88
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080089void CycleBreaker::HandleCircuit() {
90 stack_.push_back(current_vertex_);
Mike Frysinger0f9547d2012-02-16 12:11:37 -050091 CHECK_GE(stack_.size(),
92 static_cast<std::vector<Vertex::Index>::size_type>(2));
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080093 Edge min_edge = make_pair(stack_[0], stack_[1]);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070094 uint64_t min_edge_weight = kuint64max;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070095 size_t edges_considered = 0;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080096 for (vector<Vertex::Index>::const_iterator it = stack_.begin();
97 it != (stack_.end() - 1); ++it) {
98 Edge edge = make_pair(*it, *(it + 1));
99 if (cut_edges_.find(edge) != cut_edges_.end()) {
100 stack_.pop_back();
101 return;
102 }
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700103 uint64_t edge_weight = graph_utils::EdgeWeight(subgraph_, edge);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800104 if (edge_weight < min_edge_weight) {
105 min_edge_weight = edge_weight;
106 min_edge = edge;
107 }
Andrew de los Reyes45109892010-07-26 13:49:31 -0700108 edges_considered++;
109 if (edges_considered == kMaxEdgesToConsider)
110 break;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800111 }
112 cut_edges_.insert(min_edge);
113 stack_.pop_back();
114}
115
116void CycleBreaker::Unblock(Vertex::Index u) {
117 blocked_[u] = false;
118
119 for (Vertex::EdgeMap::iterator it = blocked_graph_[u].out_edges.begin();
120 it != blocked_graph_[u].out_edges.end(); ) {
121 Vertex::Index w = it->first;
122 blocked_graph_[u].out_edges.erase(it++);
123 if (blocked_[w])
124 Unblock(w);
125 }
126}
127
Andrew de los Reyes45109892010-07-26 13:49:31 -0700128bool CycleBreaker::StackContainsCutEdge() const {
129 for (std::vector<Vertex::Index>::const_iterator it = ++stack_.begin(),
Andrew de los Reyes182a9f52010-10-05 16:33:51 -0700130 e = stack_.end(); it != e; ++it) {
Andrew de los Reyes45109892010-07-26 13:49:31 -0700131 Edge edge = make_pair(*(it - 1), *it);
132 if (utils::SetContainsKey(cut_edges_, edge)) {
133 return true;
134 }
135 }
136 return false;
137}
138
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700139bool CycleBreaker::Circuit(Vertex::Index vertex, Vertex::Index depth) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800140 // "vertex" was "v" in the original paper.
141 bool found = false; // Was "f" in the original paper.
142 stack_.push_back(vertex);
143 blocked_[vertex] = true;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700144 {
145 static int counter = 0;
146 counter++;
147 if (counter == 10000) {
148 counter = 0;
149 std::string stack_str;
150 for (vector<Vertex::Index>::const_iterator it = stack_.begin();
151 it != stack_.end(); ++it) {
152 stack_str += StringPrintf("%lu -> ",
153 static_cast<long unsigned int>(*it));
154 }
155 LOG(INFO) << "stack: " << stack_str;
156 }
157 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800158
159 for (Vertex::SubgraphEdgeMap::iterator w =
160 subgraph_[vertex].subgraph_edges.begin();
161 w != subgraph_[vertex].subgraph_edges.end(); ++w) {
162 if (*w == current_vertex_) {
163 // The original paper called for printing stack_ followed by
164 // current_vertex_ here, which is a cycle. Instead, we call
165 // HandleCircuit() to break it.
166 HandleCircuit();
167 found = true;
168 } else if (!blocked_[*w]) {
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700169 if (Circuit(*w, depth + 1)) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800170 found = true;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700171 if ((depth > kMaxEdgesToConsider) || StackContainsCutEdge())
Andrew de los Reyes45109892010-07-26 13:49:31 -0700172 break;
173 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800174 }
175 }
176
177 if (found) {
178 Unblock(vertex);
179 } else {
180 for (Vertex::SubgraphEdgeMap::iterator w =
181 subgraph_[vertex].subgraph_edges.begin();
182 w != subgraph_[vertex].subgraph_edges.end(); ++w) {
Andrew de los Reyes8a51e5c2010-07-26 13:40:03 -0700183 if (blocked_graph_[*w].out_edges.find(vertex) ==
184 blocked_graph_[*w].out_edges.end()) {
185 blocked_graph_[*w].out_edges.insert(make_pair(vertex,
186 EdgeProperties()));
187 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800188 }
189 }
190 CHECK_EQ(vertex, stack_.back());
191 stack_.pop_back();
192 return found;
193}
194
195} // namespace chromeos_update_engine