]>
Commit | Line | Data |
---|---|---|
55622953 | 1 | /* |
77b1029d | 2 | * Copyright (C) 1996-2020 The Squid Software Foundation and contributors |
55622953 CT |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
9 | #include "squid.h" | |
10 | #include "CachePeer.h" | |
11 | #include "comm/Connection.h" | |
12 | #include "comm/ConnOpener.h" | |
13 | #include "ResolvedPeers.h" | |
14 | #include "SquidConfig.h" | |
15 | ||
16 | ResolvedPeers::ResolvedPeers() | |
17 | { | |
18 | if (Config.forward_max_tries > 0) | |
19 | paths_.reserve(Config.forward_max_tries); | |
20 | } | |
21 | ||
22 | void | |
23 | ResolvedPeers::retryPath(const Comm::ConnectionPointer &path) | |
24 | { | |
25 | debugs(17, 4, path); | |
26 | assert(path); | |
a4d576de EB |
27 | // Cannot use pathsToSkip for a faster (reverse) search because there |
28 | // may be unavailable paths past pathsToSkip. We could remember | |
29 | // the last extraction index, but, to completely avoid a linear search, | |
30 | // extract*() methods should return the path index. | |
31 | const auto found = std::find_if(paths_.begin(), paths_.end(), | |
32 | [path](const ResolvedPeerPath &candidate) { | |
33 | return candidate.connection == path; // (refcounted) pointer comparison | |
34 | }); | |
35 | assert(found != paths_.end()); | |
36 | assert(!found->available); | |
37 | found->available = true; | |
38 | increaseAvailability(); | |
39 | ||
40 | // if we restored availability of a path that we used to skip, update | |
41 | const auto pathsToTheLeft = static_cast<size_type>(found - paths_.begin()); | |
42 | if (pathsToTheLeft < pathsToSkip) { | |
43 | pathsToSkip = pathsToTheLeft; | |
44 | } else { | |
45 | // *found was unavailable so pathsToSkip could not end at it | |
46 | Must(pathsToTheLeft != pathsToSkip); | |
47 | } | |
55622953 CT |
48 | } |
49 | ||
50 | void | |
51 | ResolvedPeers::addPath(const Comm::ConnectionPointer &path) | |
52 | { | |
53 | paths_.emplace_back(path); | |
a4d576de EB |
54 | Must(paths_.back().available); // no pathsToSkip updates are needed |
55 | increaseAvailability(); | |
56 | } | |
57 | ||
58 | /// \returns the beginning iterator for any available-path search | |
59 | ResolvedPeers::Paths::iterator | |
60 | ResolvedPeers::start() | |
61 | { | |
62 | Must(pathsToSkip <= paths_.size()); | |
63 | return paths_.begin() + pathsToSkip; // may return end() | |
64 | } | |
65 | ||
66 | /// finalizes the iterator part of the given preliminary find*() result | |
67 | ResolvedPeers::Finding | |
68 | ResolvedPeers::makeFinding(const Paths::iterator &path, bool foundOther) | |
69 | { | |
70 | return std::make_pair((foundOther ? paths_.end() : path), foundOther); | |
71 | } | |
72 | ||
73 | /// \returns the first available same-peer same-family Finding or <end,...> | |
74 | ResolvedPeers::Finding | |
75 | ResolvedPeers::findPrime(const Comm::Connection ¤tPeer) | |
76 | { | |
77 | const auto path = start(); | |
78 | const auto foundNextOrSpare = path != paths_.end() && | |
79 | (currentPeer.getPeer() != path->connection->getPeer() || // next peer | |
80 | ConnectionFamily(currentPeer) != ConnectionFamily(*path->connection)); | |
81 | return makeFinding(path, foundNextOrSpare); | |
82 | } | |
83 | ||
84 | /// \returns the first available same-peer different-family Finding or <end,...> | |
85 | ResolvedPeers::Finding | |
86 | ResolvedPeers::findSpare(const Comm::Connection ¤tPeer) | |
87 | { | |
88 | const auto primeFamily = ConnectionFamily(currentPeer); | |
89 | const auto primePeer = currentPeer.getPeer(); | |
90 | const auto path = std::find_if(start(), paths_.end(), | |
91 | [primeFamily, primePeer](const ResolvedPeerPath &candidate) { | |
92 | if (!candidate.available) | |
93 | return false; | |
94 | if (primePeer != candidate.connection->getPeer()) | |
95 | return true; // found next peer | |
96 | if (primeFamily != ConnectionFamily(*candidate.connection)) | |
97 | return true; // found spare | |
98 | return false; | |
99 | }); | |
100 | const auto foundNext = path != paths_.end() && | |
101 | primePeer != path->connection->getPeer(); | |
102 | return makeFinding(path, foundNext); | |
103 | } | |
104 | ||
105 | /// \returns the first available same-peer Finding or <end,...> | |
106 | ResolvedPeers::Finding | |
107 | ResolvedPeers::findPeer(const Comm::Connection ¤tPeer) | |
108 | { | |
109 | const auto path = start(); | |
110 | const auto foundNext = path != paths_.end() && | |
111 | currentPeer.getPeer() != path->connection->getPeer(); | |
112 | return makeFinding(path, foundNext); | |
55622953 CT |
113 | } |
114 | ||
115 | Comm::ConnectionPointer | |
116 | ResolvedPeers::extractFront() | |
117 | { | |
118 | Must(!empty()); | |
a4d576de | 119 | return extractFound("first: ", start()); |
55622953 CT |
120 | } |
121 | ||
122 | Comm::ConnectionPointer | |
123 | ResolvedPeers::extractPrime(const Comm::Connection ¤tPeer) | |
124 | { | |
a4d576de EB |
125 | const auto found = findPrime(currentPeer).first; |
126 | if (found != paths_.end()) | |
127 | return extractFound("same-peer same-family match: ", found); | |
55622953 CT |
128 | |
129 | debugs(17, 7, "no same-peer same-family paths"); | |
130 | return nullptr; | |
131 | } | |
132 | ||
55622953 CT |
133 | Comm::ConnectionPointer |
134 | ResolvedPeers::extractSpare(const Comm::Connection ¤tPeer) | |
135 | { | |
a4d576de EB |
136 | const auto found = findSpare(currentPeer).first; |
137 | if (found != paths_.end()) | |
55622953 CT |
138 | return extractFound("same-peer different-family match: ", found); |
139 | ||
140 | debugs(17, 7, "no same-peer different-family paths"); | |
141 | return nullptr; | |
142 | } | |
143 | ||
144 | /// convenience method to finish a successful extract*() call | |
145 | Comm::ConnectionPointer | |
a4d576de | 146 | ResolvedPeers::extractFound(const char *description, const Paths::iterator &found) |
55622953 | 147 | { |
a4d576de EB |
148 | auto &path = *found; |
149 | debugs(17, 7, description << path.connection); | |
150 | assert(path.available); | |
151 | path.available = false; | |
152 | decreaseAvailability(); | |
153 | ||
154 | // if we extracted the left-most available path, find the next one | |
155 | if (static_cast<size_type>(found - paths_.begin()) == pathsToSkip) { | |
156 | while (++pathsToSkip < paths_.size() && !paths_[pathsToSkip].available) {} | |
157 | } | |
158 | ||
159 | return path.connection; | |
55622953 CT |
160 | } |
161 | ||
162 | bool | |
163 | ResolvedPeers::haveSpare(const Comm::Connection ¤tPeer) | |
164 | { | |
a4d576de EB |
165 | return findSpare(currentPeer).first != paths_.end(); |
166 | } | |
167 | ||
168 | /// whether paths_ have no (and will have no) Xs for the current peer based on | |
169 | /// the given findX(current peer) result | |
170 | bool | |
171 | ResolvedPeers::doneWith(const Finding &findings) const | |
172 | { | |
173 | if (findings.first != paths_.end()) | |
174 | return false; // not done because the caller found a viable path X | |
175 | ||
176 | // The caller did not find any path X. If the caller found any "other" | |
177 | // paths, then we are done with paths X. If there are no "other" paths, | |
178 | // then destinationsFinalized is the answer. | |
179 | return findings.second ? true : destinationsFinalized; | |
55622953 CT |
180 | } |
181 | ||
182 | bool | |
183 | ResolvedPeers::doneWithSpares(const Comm::Connection ¤tPeer) | |
184 | { | |
a4d576de | 185 | return doneWith(findSpare(currentPeer)); |
55622953 CT |
186 | } |
187 | ||
188 | bool | |
a4d576de | 189 | ResolvedPeers::doneWithPrimes(const Comm::Connection ¤tPeer) |
55622953 | 190 | { |
a4d576de | 191 | return doneWith(findPrime(currentPeer)); |
55622953 CT |
192 | } |
193 | ||
194 | bool | |
a4d576de | 195 | ResolvedPeers::doneWithPeer(const Comm::Connection ¤tPeer) |
55622953 | 196 | { |
a4d576de | 197 | return doneWith(findPeer(currentPeer)); |
55622953 CT |
198 | } |
199 | ||
200 | int | |
201 | ResolvedPeers::ConnectionFamily(const Comm::Connection &conn) | |
202 | { | |
203 | return conn.remote.isIPv4() ? AF_INET : AF_INET6; | |
204 | } | |
205 | ||
a4d576de EB |
206 | /// increments the number of available paths |
207 | void | |
208 | ResolvedPeers::increaseAvailability() | |
209 | { | |
210 | ++availablePaths; | |
211 | assert(availablePaths <= paths_.size()); | |
212 | } | |
213 | ||
214 | /// decrements the number of available paths | |
215 | void | |
216 | ResolvedPeers::decreaseAvailability() | |
217 | { | |
218 | assert(availablePaths > 0); | |
219 | --availablePaths; | |
220 | } | |
221 | ||
55622953 CT |
222 | std::ostream & |
223 | operator <<(std::ostream &os, const ResolvedPeers &peers) | |
224 | { | |
225 | if (peers.empty()) | |
226 | return os << "[no paths]"; | |
227 | return os << peers.size() << (peers.destinationsFinalized ? "" : "+") << " paths"; | |
228 | } | |
229 |