]>
Commit | Line | Data |
---|---|---|
51fdcbd5 | 1 | /* |
5b74111a | 2 | * Copyright (C) 1996-2018 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
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. | |
51fdcbd5 | 7 | */ |
603a02fd | 8 | |
bbc27441 AJ |
9 | /* DEBUG: section 48 Persistent Connections */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
e8dca475 | 12 | #include "CachePeer.h" |
582c2af2 | 13 | #include "comm.h" |
e0d28505 | 14 | #include "comm/Connection.h" |
7e66d5e2 | 15 | #include "comm/Read.h" |
c4ad1349 | 16 | #include "fd.h" |
582c2af2 FC |
17 | #include "fde.h" |
18 | #include "globals.h" | |
a750e510 | 19 | #include "mgr/Registration.h" |
e8dca475 | 20 | #include "neighbors.h" |
781ce8ff | 21 | #include "pconn.h" |
e8dca475 | 22 | #include "PeerPoolMgr.h" |
4d5904f7 | 23 | #include "SquidConfig.h" |
582c2af2 | 24 | #include "Store.h" |
603a02fd | 25 | |
f53969cc | 26 | #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */ |
62e76326 | 27 | |
642a305c | 28 | //TODO: re-attach to MemPools. WAS: static MemAllocator *pconn_fds_pool = NULL; |
62ee09ca | 29 | PconnModule * PconnModule::instance = NULL; |
781ce8ff | 30 | CBDATA_CLASS_INIT(IdleConnList); |
fa80a8ef | 31 | |
781ce8ff | 32 | /* ========== IdleConnList ============================================ */ |
603a02fd | 33 | |
a27fcaed | 34 | IdleConnList::IdleConnList(const char *aKey, PconnPool *thePool) : |
f53969cc SM |
35 | capacity_(PCONN_FDS_SZ), |
36 | size_(0), | |
37 | parent_(thePool) | |
603a02fd | 38 | { |
a27fcaed CT |
39 | //Initialize hash_link members |
40 | key = xstrdup(aKey); | |
41 | next = NULL; | |
42 | ||
e3981652 | 43 | theList_ = new Comm::ConnectionPointer[capacity_]; |
a27fcaed | 44 | |
b856803f | 45 | registerRunner(); |
a27fcaed | 46 | |
e3981652 | 47 | // TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc(); |
603a02fd | 48 | } |
49 | ||
781ce8ff | 50 | IdleConnList::~IdleConnList() |
603a02fd | 51 | { |
9815f129 AJ |
52 | if (parent_) |
53 | parent_->unlinkList(this); | |
62e76326 | 54 | |
e8dca475 CT |
55 | if (size_) { |
56 | parent_ = NULL; // prevent reentrant notifications and deletions | |
57 | closeN(size_); | |
58 | } | |
59 | ||
e3981652 | 60 | delete[] theList_; |
62e76326 | 61 | |
a27fcaed | 62 | xfree(key); |
603a02fd | 63 | } |
64 | ||
80463bb4 AJ |
65 | /** Search the list. Matches by FD socket number. |
66 | * Performed from the end of list where newest entries are. | |
67 | * | |
68 | * \retval <0 The connection is not listed | |
69 | * \retval >=0 The connection array index | |
70 | */ | |
781ce8ff | 71 | int |
80463bb4 | 72 | IdleConnList::findIndexOf(const Comm::ConnectionPointer &conn) const |
603a02fd | 73 | { |
e3981652 AJ |
74 | for (int index = size_ - 1; index >= 0; --index) { |
75 | if (conn->fd == theList_[index]->fd) { | |
80463bb4 | 76 | debugs(48, 3, HERE << "found " << conn << " at index " << index); |
139d9221 | 77 | return index; |
80463bb4 | 78 | } |
603a02fd | 79 | } |
62e76326 | 80 | |
80463bb4 | 81 | debugs(48, 2, HERE << conn << " NOT FOUND!"); |
781ce8ff | 82 | return -1; |
c4b7a5a9 | 83 | } |
84 | ||
80463bb4 | 85 | /** Remove the entry at specified index. |
a8c28b85 | 86 | * May perform a shuffle of list entries to fill the gap. |
80463bb4 AJ |
87 | * \retval false The index is not an in-use entry. |
88 | */ | |
68720ca2 | 89 | bool |
80463bb4 | 90 | IdleConnList::removeAt(int index) |
c4b7a5a9 | 91 | { |
e3981652 | 92 | if (index < 0 || index >= size_) |
68720ca2 | 93 | return false; |
c4b7a5a9 | 94 | |
80463bb4 | 95 | // shuffle the remaining entries to fill the new gap. |
5db6bf73 | 96 | for (; index < size_ - 1; ++index) |
e3981652 | 97 | theList_[index] = theList_[index + 1]; |
4eb4fb17 | 98 | theList_[--size_] = NULL; |
62e76326 | 99 | |
9815f129 | 100 | if (parent_) { |
983983ce | 101 | parent_->noteConnectionRemoved(); |
4eb4fb17 | 102 | if (size_ == 0) { |
a27fcaed | 103 | debugs(48, 3, "deleting " << hashKeyStr(this)); |
4eb4fb17 CT |
104 | delete this; |
105 | } | |
8ea9f779 | 106 | } |
2dba5b8e | 107 | |
9815f129 | 108 | return true; |
f915be8d AJ |
109 | } |
110 | ||
111 | // almost a duplicate of removeFD. But drops multiple entries. | |
112 | void | |
113 | IdleConnList::closeN(size_t n) | |
114 | { | |
115 | if (n < 1) { | |
116 | debugs(48, 2, HERE << "Nothing to do."); | |
117 | return; | |
8dc322e4 | 118 | } else if (n >= (size_t)size_) { |
f915be8d | 119 | debugs(48, 2, HERE << "Closing all entries."); |
8dc322e4 | 120 | while (size_ > 0) { |
4eb4fb17 | 121 | const Comm::ConnectionPointer conn = theList_[--size_]; |
9815f129 AJ |
122 | theList_[size_] = NULL; |
123 | clearHandlers(conn); | |
124 | conn->close(); | |
125 | if (parent_) | |
126 | parent_->noteConnectionRemoved(); | |
f915be8d | 127 | } |
8dc322e4 | 128 | } else { //if (n < size_) |
9815f129 | 129 | debugs(48, 2, HERE << "Closing " << n << " of " << size_ << " entries."); |
f915be8d | 130 | |
8dc322e4 | 131 | size_t index; |
f915be8d | 132 | // ensure the first N entries are closed |
5db6bf73 | 133 | for (index = 0; index < n; ++index) { |
8dc322e4 CT |
134 | const Comm::ConnectionPointer conn = theList_[index]; |
135 | theList_[index] = NULL; | |
9815f129 AJ |
136 | clearHandlers(conn); |
137 | conn->close(); | |
138 | if (parent_) | |
139 | parent_->noteConnectionRemoved(); | |
f915be8d AJ |
140 | } |
141 | // shuffle the list N down. | |
5db6bf73 | 142 | for (index = 0; index < (size_t)size_ - n; ++index) { |
8dc322e4 | 143 | theList_[index] = theList_[index + n]; |
f915be8d AJ |
144 | } |
145 | // ensure the last N entries are unset | |
8dc322e4 | 146 | while (index < ((size_t)size_)) { |
f412b2d6 FC |
147 | theList_[index] = NULL; |
148 | ++index; | |
f915be8d | 149 | } |
8dc322e4 | 150 | size_ -= n; |
f915be8d AJ |
151 | } |
152 | ||
9815f129 | 153 | if (parent_ && size_ == 0) { |
a27fcaed | 154 | debugs(48, 3, "deleting " << hashKeyStr(this)); |
781ce8ff | 155 | delete this; |
156 | } | |
c4b7a5a9 | 157 | } |
158 | ||
781ce8ff | 159 | void |
642a305c | 160 | IdleConnList::clearHandlers(const Comm::ConnectionPointer &conn) |
781ce8ff | 161 | { |
5be728d4 | 162 | debugs(48, 3, HERE << "removing close handler for " << conn); |
80463bb4 | 163 | comm_read_cancel(conn->fd, IdleConnList::Read, this); |
8d77a37c | 164 | commUnsetConnTimeout(conn); |
781ce8ff | 165 | } |
62e76326 | 166 | |
781ce8ff | 167 | void |
642a305c | 168 | IdleConnList::push(const Comm::ConnectionPointer &conn) |
c4b7a5a9 | 169 | { |
e3981652 AJ |
170 | if (size_ == capacity_) { |
171 | debugs(48, 3, HERE << "growing idle Connection array"); | |
172 | capacity_ <<= 1; | |
173 | const Comm::ConnectionPointer *oldList = theList_; | |
174 | theList_ = new Comm::ConnectionPointer[capacity_]; | |
5db6bf73 | 175 | for (int index = 0; index < size_; ++index) |
e3981652 | 176 | theList_[index] = oldList[index]; |
781ce8ff | 177 | |
642a305c | 178 | delete[] oldList; |
781ce8ff | 179 | } |
62e76326 | 180 | |
983983ce AJ |
181 | if (parent_) |
182 | parent_->noteConnectionAdded(); | |
2dba5b8e | 183 | |
f412b2d6 FC |
184 | theList_[size_] = conn; |
185 | ++size_; | |
8d77a37c | 186 | AsyncCall::Pointer readCall = commCbCall(5,4, "IdleConnList::Read", |
dc49061a | 187 | CommIoCbPtrFun(IdleConnList::Read, this)); |
8d77a37c | 188 | comm_read(conn, fakeReadBuf_, sizeof(fakeReadBuf_), readCall); |
4eb4fb17 | 189 | AsyncCall::Pointer timeoutCall = commCbCall(5,4, "IdleConnList::Timeout", |
dc49061a | 190 | CommTimeoutCbPtrFun(IdleConnList::Timeout, this)); |
c5c06f02 | 191 | commSetConnTimeout(conn, conn->timeLeft(Config.Timeout.serverIdlePconn), timeoutCall); |
603a02fd | 192 | } |
193 | ||
a8c28b85 AJ |
194 | /// Determine whether an entry in the idle list is available for use. |
195 | /// Returns false if the entry is unset, closed or closing. | |
196 | bool | |
197 | IdleConnList::isAvailable(int i) const | |
198 | { | |
199 | const Comm::ConnectionPointer &conn = theList_[i]; | |
200 | ||
201 | // connection already closed. useless. | |
202 | if (!Comm::IsConnOpen(conn)) | |
203 | return false; | |
204 | ||
205 | // our connection early-read/close handler is scheduled to run already. unsafe | |
206 | if (!COMMIO_FD_READCB(conn->fd)->active()) | |
207 | return false; | |
208 | ||
209 | return true; | |
210 | } | |
211 | ||
9815f129 AJ |
212 | Comm::ConnectionPointer |
213 | IdleConnList::pop() | |
214 | { | |
5e263176 | 215 | for (int i=size_-1; i>=0; --i) { |
9815f129 | 216 | |
a8c28b85 AJ |
217 | if (!isAvailable(i)) |
218 | continue; | |
219 | ||
220 | // our connection timeout handler is scheduled to run already. unsafe for now. | |
221 | // TODO: cancel the pending timeout callback and allow re-use of the conn. | |
222 | if (fd_table[theList_[i]->fd].timeoutHandler == NULL) | |
9815f129 AJ |
223 | continue; |
224 | ||
225 | // finally, a match. pop and return it. | |
226 | Comm::ConnectionPointer result = theList_[i]; | |
f7605425 | 227 | clearHandlers(result); |
9815f129 AJ |
228 | /* may delete this */ |
229 | removeAt(i); | |
230 | return result; | |
231 | } | |
232 | ||
233 | return Comm::ConnectionPointer(); | |
603a02fd | 234 | } |
235 | ||
781ce8ff | 236 | /* |
237 | * XXX this routine isn't terribly efficient - if there's a pending | |
238 | * read event (which signifies the fd will close in the next IO loop!) | |
239 | * we ignore the FD and move onto the next one. This means, as an example, | |
240 | * if we have a lot of FDs open to a very popular server and we get a bunch | |
241 | * of requests JUST as they timeout (say, it shuts down) we'll be wasting | |
242 | * quite a bit of CPU. Just keep it in mind. | |
243 | */ | |
642a305c | 244 | Comm::ConnectionPointer |
a27fcaed | 245 | IdleConnList::findUseable(const Comm::ConnectionPointer &aKey) |
603a02fd | 246 | { |
e3981652 | 247 | assert(size_); |
62e76326 | 248 | |
e884bbde | 249 | // small optimization: do the constant bool tests only once. |
a27fcaed CT |
250 | const bool keyCheckAddr = !aKey->local.isAnyAddr(); |
251 | const bool keyCheckPort = aKey->local.port() > 0; | |
e884bbde | 252 | |
5e263176 | 253 | for (int i=size_-1; i>=0; --i) { |
139d9221 | 254 | |
a8c28b85 | 255 | if (!isAvailable(i)) |
139d9221 AJ |
256 | continue; |
257 | ||
258 | // local end port is required, but dont match. | |
a27fcaed | 259 | if (keyCheckPort && aKey->local.port() != theList_[i]->local.port()) |
139d9221 AJ |
260 | continue; |
261 | ||
262 | // local address is required, but does not match. | |
a27fcaed | 263 | if (keyCheckAddr && aKey->local.matchIPAddr(theList_[i]->local) != 0) |
139d9221 AJ |
264 | continue; |
265 | ||
a8c28b85 AJ |
266 | // our connection timeout handler is scheduled to run already. unsafe for now. |
267 | // TODO: cancel the pending timeout callback and allow re-use of the conn. | |
268 | if (fd_table[theList_[i]->fd].timeoutHandler == NULL) | |
269 | continue; | |
270 | ||
80463bb4 | 271 | // finally, a match. pop and return it. |
e3981652 | 272 | Comm::ConnectionPointer result = theList_[i]; |
f7605425 | 273 | clearHandlers(result); |
80463bb4 AJ |
274 | /* may delete this */ |
275 | removeAt(i); | |
276 | return result; | |
781ce8ff | 277 | } |
278 | ||
80463bb4 | 279 | return Comm::ConnectionPointer(); |
603a02fd | 280 | } |
281 | ||
a8c28b85 AJ |
282 | /* might delete list */ |
283 | void | |
284 | IdleConnList::findAndClose(const Comm::ConnectionPointer &conn) | |
285 | { | |
286 | const int index = findIndexOf(conn); | |
287 | if (index >= 0) { | |
e8dca475 CT |
288 | if (parent_) |
289 | parent_->notifyManager("idle conn closure"); | |
f7605425 | 290 | clearHandlers(conn); |
a8c28b85 AJ |
291 | /* might delete this */ |
292 | removeAt(index); | |
a8c28b85 AJ |
293 | conn->close(); |
294 | } | |
295 | } | |
296 | ||
781ce8ff | 297 | void |
ced8def3 | 298 | IdleConnList::Read(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int, void *data) |
603a02fd | 299 | { |
642a305c | 300 | debugs(48, 3, HERE << len << " bytes from " << conn); |
62e76326 | 301 | |
c8407295 AJ |
302 | if (flag == Comm::ERR_CLOSING) { |
303 | debugs(48, 3, HERE << "Comm::ERR_CLOSING from " << conn); | |
304 | /* Bail out on Comm::ERR_CLOSING - may happen when shutdown aborts our idle FD */ | |
62e76326 | 305 | return; |
c4b7a5a9 | 306 | } |
307 | ||
781ce8ff | 308 | IdleConnList *list = (IdleConnList *) data; |
a8c28b85 AJ |
309 | /* may delete list/data */ |
310 | list->findAndClose(conn); | |
603a02fd | 311 | } |
312 | ||
781ce8ff | 313 | void |
8d77a37c | 314 | IdleConnList::Timeout(const CommTimeoutCbParams &io) |
5ba18ba6 | 315 | { |
8d77a37c AJ |
316 | debugs(48, 3, HERE << io.conn); |
317 | IdleConnList *list = static_cast<IdleConnList *>(io.data); | |
a8c28b85 AJ |
318 | /* may delete list/data */ |
319 | list->findAndClose(io.conn); | |
781ce8ff | 320 | } |
62e76326 | 321 | |
a27fcaed CT |
322 | void |
323 | IdleConnList::endingShutdown() | |
324 | { | |
325 | closeN(size_); | |
326 | } | |
327 | ||
781ce8ff | 328 | /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */ |
62e76326 | 329 | |
781ce8ff | 330 | const char * |
642a305c | 331 | PconnPool::key(const Comm::ConnectionPointer &destLink, const char *domain) |
781ce8ff | 332 | { |
06093389 | 333 | LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 3 + 10); |
781ce8ff | 334 | |
4dd643d5 | 335 | destLink->remote.toUrl(buf, SQUIDHOSTNAMELEN * 3 + 10); |
642a305c | 336 | if (domain) { |
80463bb4 | 337 | const int used = strlen(buf); |
642a305c AJ |
338 | snprintf(buf+used, SQUIDHOSTNAMELEN * 3 + 10-used, "/%s", domain); |
339 | } | |
340 | ||
341 | debugs(48,6,"PconnPool::key(" << destLink << ", " << (domain?domain:"[no domain]") << ") is {" << buf << "}" ); | |
781ce8ff | 342 | return buf; |
343 | } | |
62e76326 | 344 | |
781ce8ff | 345 | void |
642a305c | 346 | PconnPool::dumpHist(StoreEntry * e) const |
781ce8ff | 347 | { |
5ba18ba6 | 348 | storeAppendPrintf(e, |
781ce8ff | 349 | "%s persistent connection counts:\n" |
62e76326 | 350 | "\n" |
e87619bc AJ |
351 | "\t Requests\t Connection Count\n" |
352 | "\t --------\t ----------------\n", | |
781ce8ff | 353 | descr); |
62e76326 | 354 | |
5db6bf73 | 355 | for (int i = 0; i < PCONN_HIST_SZ; ++i) { |
781ce8ff | 356 | if (hist[i] == 0) |
62e76326 | 357 | continue; |
358 | ||
e87619bc | 359 | storeAppendPrintf(e, "\t%d\t%d\n", i, hist[i]); |
5ba18ba6 | 360 | } |
361 | } | |
603a02fd | 362 | |
06093389 | 363 | void |
642a305c | 364 | PconnPool::dumpHash(StoreEntry *e) const |
06093389 | 365 | { |
06093389 AJ |
366 | hash_table *hid = table; |
367 | hash_first(hid); | |
368 | ||
642a305c | 369 | int i = 0; |
e8dca475 | 370 | for (hash_link *walker = hash_next(hid); walker; walker = hash_next(hid)) { |
e87619bc | 371 | storeAppendPrintf(e, "\t item %d:\t%s\n", i, (char *)(walker->key)); |
5db6bf73 | 372 | ++i; |
06093389 AJ |
373 | } |
374 | } | |
375 | ||
781ce8ff | 376 | /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */ |
603a02fd | 377 | |
e8dca475 | 378 | PconnPool::PconnPool(const char *aDescr, const CbcPointer<PeerPoolMgr> &aMgr): |
f53969cc SM |
379 | table(NULL), descr(aDescr), |
380 | mgr(aMgr), | |
381 | theCount(0) | |
603a02fd | 382 | { |
ed7f5615 | 383 | int i; |
30abd221 | 384 | table = hash_create((HASHCMP *) strcmp, 229, hash_string); |
62e76326 | 385 | |
5db6bf73 | 386 | for (i = 0; i < PCONN_HIST_SZ; ++i) |
781ce8ff | 387 | hist[i] = 0; |
62e76326 | 388 | |
7a56c509 AJ |
389 | PconnModule::GetInstance()->add(this); |
390 | } | |
391 | ||
e8dca475 CT |
392 | static void |
393 | DeleteIdleConnList(void *hashItem) | |
394 | { | |
af834148 | 395 | delete static_cast<IdleConnList*>(hashItem); |
e8dca475 CT |
396 | } |
397 | ||
7a56c509 AJ |
398 | PconnPool::~PconnPool() |
399 | { | |
e8dca475 CT |
400 | PconnModule::GetInstance()->remove(this); |
401 | hashFreeItems(table, &DeleteIdleConnList); | |
7a56c509 | 402 | hashFreeMemory(table); |
e8dca475 | 403 | descr = NULL; |
603a02fd | 404 | } |
405 | ||
406 | void | |
642a305c | 407 | PconnPool::push(const Comm::ConnectionPointer &conn, const char *domain) |
603a02fd | 408 | { |
9e008dda | 409 | if (fdUsageHigh()) { |
80463bb4 AJ |
410 | debugs(48, 3, HERE << "Not many unused FDs"); |
411 | conn->close(); | |
62e76326 | 412 | return; |
9e008dda | 413 | } else if (shutting_down) { |
80463bb4 AJ |
414 | conn->close(); |
415 | debugs(48, 3, HERE << "Squid is shutting down. Refusing to do anything"); | |
62e76326 | 416 | return; |
47c9d579 | 417 | } |
e8dca475 | 418 | // TODO: also close used pconns if we exceed peer max-conn limit |
62e76326 | 419 | |
642a305c AJ |
420 | const char *aKey = key(conn, domain); |
421 | IdleConnList *list = (IdleConnList *) hash_lookup(table, aKey); | |
62e76326 | 422 | |
9e008dda | 423 | if (list == NULL) { |
781ce8ff | 424 | list = new IdleConnList(aKey, this); |
a27fcaed CT |
425 | debugs(48, 3, "new IdleConnList for {" << hashKeyStr(list) << "}" ); |
426 | hash_join(table, list); | |
06093389 | 427 | } else { |
a27fcaed | 428 | debugs(48, 3, "found IdleConnList for {" << hashKeyStr(list) << "}" ); |
603a02fd | 429 | } |
62e76326 | 430 | |
642a305c AJ |
431 | list->push(conn); |
432 | assert(!comm_has_incomplete_write(conn->fd)); | |
781ce8ff | 433 | |
642a305c | 434 | LOCAL_ARRAY(char, desc, FD_DESC_SZ); |
97b32442 | 435 | snprintf(desc, FD_DESC_SZ, "Idle server: %s", aKey); |
642a305c AJ |
436 | fd_note(conn->fd, desc); |
437 | debugs(48, 3, HERE << "pushed " << conn << " for " << aKey); | |
e8dca475 CT |
438 | |
439 | // successful push notifications resume multi-connection opening sequence | |
440 | notifyManager("push"); | |
603a02fd | 441 | } |
442 | ||
80463bb4 | 443 | Comm::ConnectionPointer |
e8dca475 | 444 | PconnPool::pop(const Comm::ConnectionPointer &dest, const char *domain, bool keepOpen) |
603a02fd | 445 | { |
e8dca475 CT |
446 | |
447 | const char * aKey = key(dest, domain); | |
62e76326 | 448 | |
06093389 AJ |
449 | IdleConnList *list = (IdleConnList *)hash_lookup(table, aKey); |
450 | if (list == NULL) { | |
80463bb4 | 451 | debugs(48, 3, HERE << "lookup for key {" << aKey << "} failed."); |
e8dca475 CT |
452 | // failure notifications resume standby conn creation after fdUsageHigh |
453 | notifyManager("pop failure"); | |
80463bb4 | 454 | return Comm::ConnectionPointer(); |
af6a12ee | 455 | } else { |
a27fcaed | 456 | debugs(48, 3, "found " << hashKeyStr(list) << |
e8dca475 | 457 | (keepOpen ? " to use" : " to kill")); |
06093389 | 458 | } |
781ce8ff | 459 | |
80463bb4 | 460 | /* may delete list */ |
e8dca475 CT |
461 | Comm::ConnectionPointer popped = list->findUseable(dest); |
462 | if (!keepOpen && Comm::IsConnOpen(popped)) | |
463 | popped->close(); | |
464 | ||
465 | // successful pop notifications replenish standby connections pool | |
466 | notifyManager("pop"); | |
467 | return popped; | |
468 | } | |
781ce8ff | 469 | |
e8dca475 CT |
470 | void |
471 | PconnPool::notifyManager(const char *reason) | |
472 | { | |
473 | if (mgr.valid()) | |
474 | PeerPoolMgr::Checkpoint(mgr, reason); | |
603a02fd | 475 | } |
ed7f5615 | 476 | |
477 | void | |
e8dca475 | 478 | PconnPool::closeN(int n) |
2dba5b8e | 479 | { |
e8dca475 CT |
480 | hash_table *hid = table; |
481 | hash_first(hid); | |
482 | ||
483 | // close N connections, one per list, to treat all lists "fairly" | |
484 | for (int i = 0; i < n && count(); ++i) { | |
485 | ||
486 | hash_link *current = hash_next(hid); | |
487 | if (!current) { | |
488 | hash_first(hid); | |
489 | current = hash_next(hid); | |
490 | Must(current); // must have one because the count() was positive | |
491 | } | |
492 | ||
493 | // may delete current | |
af834148 | 494 | static_cast<IdleConnList*>(current)->closeN(1); |
e8dca475 | 495 | } |
2dba5b8e CT |
496 | } |
497 | ||
498 | void | |
499 | PconnPool::unlinkList(IdleConnList *list) | |
ed7f5615 | 500 | { |
2dba5b8e CT |
501 | theCount -= list->count(); |
502 | assert(theCount >= 0); | |
a27fcaed | 503 | hash_remove_link(table, list); |
781ce8ff | 504 | } |
505 | ||
506 | void | |
2dba5b8e | 507 | PconnPool::noteUses(int uses) |
781ce8ff | 508 | { |
509 | if (uses >= PCONN_HIST_SZ) | |
510 | uses = PCONN_HIST_SZ - 1; | |
511 | ||
5db6bf73 | 512 | ++hist[uses]; |
781ce8ff | 513 | } |
514 | ||
515 | /* ========== PconnModule ============================================ */ | |
516 | ||
517 | /* | |
518 | * This simple class exists only for the cache manager | |
519 | */ | |
520 | ||
e8dca475 | 521 | PconnModule::PconnModule(): pools() |
781ce8ff | 522 | { |
6852be71 | 523 | registerWithCacheManager(); |
781ce8ff | 524 | } |
525 | ||
62ee09ca | 526 | PconnModule * |
527 | PconnModule::GetInstance() | |
528 | { | |
529 | if (instance == NULL) | |
530 | instance = new PconnModule; | |
531 | ||
532 | return instance; | |
533 | } | |
534 | ||
535 | void | |
15fab853 | 536 | PconnModule::registerWithCacheManager(void) |
62ee09ca | 537 | { |
8822ebee | 538 | Mgr::RegisterAction("pconn", |
d9fc6862 A |
539 | "Persistent Connection Utilization Histograms", |
540 | DumpWrapper, 0, 1); | |
62ee09ca | 541 | } |
542 | ||
781ce8ff | 543 | void |
642a305c | 544 | PconnModule::add(PconnPool *aPool) |
781ce8ff | 545 | { |
e8dca475 | 546 | pools.insert(aPool); |
781ce8ff | 547 | } |
548 | ||
549 | void | |
e8dca475 | 550 | PconnModule::remove(PconnPool *aPool) |
781ce8ff | 551 | { |
e8dca475 CT |
552 | pools.erase(aPool); |
553 | } | |
781ce8ff | 554 | |
e8dca475 CT |
555 | void |
556 | PconnModule::dump(StoreEntry *e) | |
557 | { | |
558 | typedef Pools::const_iterator PCI; | |
559 | int i = 0; // TODO: Why number pools if they all have names? | |
560 | for (PCI p = pools.begin(); p != pools.end(); ++p, ++i) { | |
561 | // TODO: Let each pool dump itself the way it wants to. | |
06093389 | 562 | storeAppendPrintf(e, "\n Pool %d Stats\n", i); |
e8dca475 | 563 | (*p)->dumpHist(e); |
06093389 | 564 | storeAppendPrintf(e, "\n Pool %d Hash Table\n",i); |
e8dca475 | 565 | (*p)->dumpHash(e); |
781ce8ff | 566 | } |
567 | } | |
568 | ||
62ee09ca | 569 | void |
570 | PconnModule::DumpWrapper(StoreEntry *e) | |
781ce8ff | 571 | { |
62ee09ca | 572 | PconnModule::GetInstance()->dump(e); |
ed7f5615 | 573 | } |
f53969cc | 574 |