blob: 906502d3ea086eedcc7747797da24dec9ff87e92 [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
Alex Deymo161c4a12014-05-16 15:56:21 -07005#include "update_engine/payload_generator/cycle_breaker.h"
6
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -07007#include <inttypes.h>
Alex Deymo161c4a12014-05-16 15:56:21 -07008
Andrew de los Reyes35a7af12010-03-10 16:33:26 -08009#include <set>
Alex Vakulenko072359c2014-07-18 11:41:07 -070010#include <string>
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080011#include <utility>
Alex Deymo161c4a12014-05-16 15:56:21 -070012
Alex Vakulenko75039d72014-03-25 12:36:28 -070013#include <base/strings/string_util.h>
14#include <base/strings/stringprintf.h>
Alex Deymo161c4a12014-05-16 15:56:21 -070015
16#include "update_engine/payload_generator/graph_utils.h"
17#include "update_engine/payload_generator/tarjan.h"
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080018#include "update_engine/utils.h"
19
20using std::make_pair;
21using std::set;
22using std::vector;
23
24namespace chromeos_update_engine {
25
26// This is the outer function from the original paper.
27void CycleBreaker::BreakCycles(const Graph& graph, set<Edge>* out_cut_edges) {
28 cut_edges_.clear();
Alex Deymo161c4a12014-05-16 15:56:21 -070029
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080030 // Make a copy, which we will modify by removing edges. Thus, in each
31 // iteration subgraph_ is the current subgraph or the original with
32 // vertices we desire. This variable was "A_K" in the original paper.
33 subgraph_ = graph;
Alex Deymo161c4a12014-05-16 15:56:21 -070034
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080035 // The paper calls for the "adjacency structure (i.e., graph) of
36 // strong (-ly connected) component K with least vertex in subgraph
37 // induced by {s, s + 1, ..., n}".
38 // We arbitrarily order each vertex by its index in the graph. Thus,
39 // each iteration, we are looking at the subgraph {s, s + 1, ..., n}
40 // and looking for the strongly connected component with vertex s.
41
42 TarjanAlgorithm tarjan;
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070043 skipped_ops_ = 0;
Alex Deymo161c4a12014-05-16 15:56:21 -070044
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080045 for (Graph::size_type i = 0; i < subgraph_.size(); i++) {
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070046 DeltaArchiveManifest_InstallOperation_Type op_type = graph[i].op.type();
47 if (op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
48 op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
49 skipped_ops_++;
50 continue;
51 }
52
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080053 if (i > 0) {
54 // Erase node (i - 1) from subgraph_. First, erase what it points to
55 subgraph_[i - 1].out_edges.clear();
56 // Now, erase any pointers to node (i - 1)
57 for (Graph::size_type j = i; j < subgraph_.size(); j++) {
58 subgraph_[j].out_edges.erase(i - 1);
59 }
60 }
61
62 // Calculate SCC (strongly connected component) with vertex i.
63 vector<Vertex::Index> component_indexes;
64 tarjan.Execute(i, &subgraph_, &component_indexes);
65
66 // Set subgraph edges for the components in the SCC.
67 for (vector<Vertex::Index>::iterator it = component_indexes.begin();
68 it != component_indexes.end(); ++it) {
69 subgraph_[*it].subgraph_edges.clear();
70 for (vector<Vertex::Index>::iterator jt = component_indexes.begin();
71 jt != component_indexes.end(); ++jt) {
72 // If there's a link from *it -> *jt in the graph,
73 // add a subgraph_ edge
74 if (utils::MapContainsKey(subgraph_[*it].out_edges, *jt))
75 subgraph_[*it].subgraph_edges.insert(*jt);
Alex Deymo161c4a12014-05-16 15:56:21 -070076 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080077 }
78
79 current_vertex_ = i;
80 blocked_.clear();
81 blocked_.resize(subgraph_.size());
82 blocked_graph_.clear();
83 blocked_graph_.resize(subgraph_.size());
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070084 Circuit(current_vertex_, 0);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080085 }
Alex Deymo161c4a12014-05-16 15:56:21 -070086
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080087 out_cut_edges->swap(cut_edges_);
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070088 LOG(INFO) << "Cycle breaker skipped " << skipped_ops_ << " ops.";
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080089 DCHECK(stack_.empty());
90}
91
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -070092static const size_t kMaxEdgesToConsider = 2;
93
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080094void CycleBreaker::HandleCircuit() {
95 stack_.push_back(current_vertex_);
Mike Frysinger0f9547d2012-02-16 12:11:37 -050096 CHECK_GE(stack_.size(),
Alex Deymof329b932014-10-30 01:37:48 -070097 static_cast<vector<Vertex::Index>::size_type>(2));
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080098 Edge min_edge = make_pair(stack_[0], stack_[1]);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070099 uint64_t min_edge_weight = kuint64max;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700100 size_t edges_considered = 0;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800101 for (vector<Vertex::Index>::const_iterator it = stack_.begin();
102 it != (stack_.end() - 1); ++it) {
103 Edge edge = make_pair(*it, *(it + 1));
104 if (cut_edges_.find(edge) != cut_edges_.end()) {
105 stack_.pop_back();
106 return;
107 }
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700108 uint64_t edge_weight = graph_utils::EdgeWeight(subgraph_, edge);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800109 if (edge_weight < min_edge_weight) {
110 min_edge_weight = edge_weight;
111 min_edge = edge;
112 }
Andrew de los Reyes45109892010-07-26 13:49:31 -0700113 edges_considered++;
114 if (edges_considered == kMaxEdgesToConsider)
115 break;
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800116 }
117 cut_edges_.insert(min_edge);
118 stack_.pop_back();
119}
120
121void CycleBreaker::Unblock(Vertex::Index u) {
122 blocked_[u] = false;
123
124 for (Vertex::EdgeMap::iterator it = blocked_graph_[u].out_edges.begin();
125 it != blocked_graph_[u].out_edges.end(); ) {
126 Vertex::Index w = it->first;
127 blocked_graph_[u].out_edges.erase(it++);
128 if (blocked_[w])
129 Unblock(w);
130 }
131}
132
Andrew de los Reyes45109892010-07-26 13:49:31 -0700133bool CycleBreaker::StackContainsCutEdge() const {
Alex Deymof329b932014-10-30 01:37:48 -0700134 for (vector<Vertex::Index>::const_iterator it = ++stack_.begin(),
Andrew de los Reyes182a9f52010-10-05 16:33:51 -0700135 e = stack_.end(); it != e; ++it) {
Andrew de los Reyes45109892010-07-26 13:49:31 -0700136 Edge edge = make_pair(*(it - 1), *it);
137 if (utils::SetContainsKey(cut_edges_, edge)) {
138 return true;
139 }
140 }
141 return false;
142}
143
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700144bool CycleBreaker::Circuit(Vertex::Index vertex, Vertex::Index depth) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800145 // "vertex" was "v" in the original paper.
146 bool found = false; // Was "f" in the original paper.
147 stack_.push_back(vertex);
148 blocked_[vertex] = true;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700149 {
150 static int counter = 0;
151 counter++;
152 if (counter == 10000) {
153 counter = 0;
154 std::string stack_str;
Alex Vakulenko072359c2014-07-18 11:41:07 -0700155 for (Vertex::Index index : stack_) {
156 stack_str += std::to_string(index);
157 stack_str += " -> ";
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700158 }
159 LOG(INFO) << "stack: " << stack_str;
160 }
161 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800162
163 for (Vertex::SubgraphEdgeMap::iterator w =
164 subgraph_[vertex].subgraph_edges.begin();
165 w != subgraph_[vertex].subgraph_edges.end(); ++w) {
166 if (*w == current_vertex_) {
167 // The original paper called for printing stack_ followed by
168 // current_vertex_ here, which is a cycle. Instead, we call
169 // HandleCircuit() to break it.
170 HandleCircuit();
171 found = true;
172 } else if (!blocked_[*w]) {
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700173 if (Circuit(*w, depth + 1)) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800174 found = true;
Andrew de los Reyes21ab31c2010-08-19 14:59:00 -0700175 if ((depth > kMaxEdgesToConsider) || StackContainsCutEdge())
Andrew de los Reyes45109892010-07-26 13:49:31 -0700176 break;
177 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800178 }
179 }
180
181 if (found) {
182 Unblock(vertex);
183 } else {
184 for (Vertex::SubgraphEdgeMap::iterator w =
185 subgraph_[vertex].subgraph_edges.begin();
186 w != subgraph_[vertex].subgraph_edges.end(); ++w) {
Andrew de los Reyes8a51e5c2010-07-26 13:40:03 -0700187 if (blocked_graph_[*w].out_edges.find(vertex) ==
188 blocked_graph_[*w].out_edges.end()) {
189 blocked_graph_[*w].out_edges.insert(make_pair(vertex,
190 EdgeProperties()));
191 }
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800192 }
193 }
194 CHECK_EQ(vertex, stack_.back());
195 stack_.pop_back();
196 return found;
197}
198
199} // namespace chromeos_update_engine