]> git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
typos
[thirdparty/squid.git] / src / pconn.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 48 Persistent Connections
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35 #include "squid.h"
36 #include "CacheManager.h"
37 #include "comm.h"
38 #include "comm/Connection.h"
39 #include "fde.h"
40 #include "pconn.h"
41 #include "Store.h"
42
43 #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
44
45 //TODO: re-attach to MemPools. WAS: static MemAllocator *pconn_fds_pool = NULL;
46 PconnModule * PconnModule::instance = NULL;
47 CBDATA_CLASS_INIT(IdleConnList);
48
49 /* ========== IdleConnList ============================================ */
50
51 IdleConnList::IdleConnList(const char *key, PconnPool *thePool) :
52 nfds_alloc(PCONN_FDS_SZ),
53 nfds(0),
54 parent(thePool)
55 {
56 hash.key = xstrdup(key);
57 theList = new Comm::ConnectionPointer[nfds_alloc];
58 // TODO: re-attach to MemPools. WAS: fds = (int *)pconn_fds_pool->alloc();
59 }
60
61 IdleConnList::~IdleConnList()
62 {
63 parent->unlinkList(this);
64
65 /* TODO: re-attach to MemPools.
66 if (nfds_alloc == PCONN_FDS_SZ)
67 pconn_fds_pool->freeOne(theList);
68 else
69 */
70 delete[] theList;
71
72 xfree(hash.key);
73 }
74
75 int
76 IdleConnList::findIndex(const Comm::ConnectionPointer &conn)
77 {
78 for (int index = nfds - 1; index >= 0; --index) {
79 if (conn->fd == theList[index]->fd)
80 return index;
81 }
82
83 return -1;
84 }
85
86 bool
87 IdleConnList::remove(const Comm::ConnectionPointer &conn)
88 {
89 int index = findIndex(conn);
90 if (index < 0) {
91 debugs(48, 2, HERE << conn << " NOT FOUND!");
92 return false;
93 }
94 debugs(48, 3, HERE << "found " << conn << " at index " << index);
95
96 for (; index < nfds - 1; index++)
97 theList[index] = theList[index + 1];
98
99 if (--nfds == 0) {
100 debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
101 delete this;
102 }
103 return true;
104 }
105
106 void
107 IdleConnList::clearHandlers(const Comm::ConnectionPointer &conn)
108 {
109 comm_read_cancel(conn->fd, IdleConnList::read, this);
110 commSetTimeout(conn->fd, -1, NULL, NULL);
111 }
112
113 void
114 IdleConnList::push(const Comm::ConnectionPointer &conn)
115 {
116 if (nfds == nfds_alloc) {
117 debugs(48, 3, "IdleConnList::push: growing FD array");
118 nfds_alloc <<= 1;
119 const Comm::ConnectionPointer *oldList = theList;
120 theList = new Comm::ConnectionPointer[nfds_alloc];
121 for (int index = 0; index < nfds; index++)
122 theList[index] = oldList[index];
123
124 /* TODO: re-attach to MemPools.
125 if (nfds == PCONN_FDS_SZ)
126 pconn_fds_pool->freeOne(oldList);
127 else
128 */
129 delete[] oldList;
130 }
131
132 theList[nfds++] = conn;
133 comm_read(conn, fakeReadBuf, sizeof(fakeReadBuf), IdleConnList::read, this);
134 commSetTimeout(conn->fd, Config.Timeout.pconn, IdleConnList::timeout, this);
135 }
136
137 /*
138 * XXX this routine isn't terribly efficient - if there's a pending
139 * read event (which signifies the fd will close in the next IO loop!)
140 * we ignore the FD and move onto the next one. This means, as an example,
141 * if we have a lot of FDs open to a very popular server and we get a bunch
142 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
143 * quite a bit of CPU. Just keep it in mind.
144 */
145 Comm::ConnectionPointer
146 IdleConnList::findUseable(const Comm::ConnectionPointer &key)
147 {
148 assert(nfds);
149
150 for (int i=nfds-1; i>=0; i--) {
151
152 // callback pending indicates that remote end of the conn has just closed.
153 if (comm_has_pending_read_callback(theList[i]->fd))
154 continue;
155
156 // local end port is required, but dont match.
157 if (key->local.GetPort() > 0 && key->local.GetPort() != theList[i]->local.GetPort())
158 continue;
159
160 // local address is required, but does not match.
161 if (!key->local.IsAnyAddr() && key->local.matchIPAddr(theList[i]->local) != 0)
162 continue;
163
164 // finally, a match
165 return theList[i];
166 }
167
168 return key;
169 }
170
171 void
172 IdleConnList::read(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
173 {
174 debugs(48, 3, HERE << len << " bytes from " << conn);
175
176 if (flag == COMM_ERR_CLOSING) {
177 /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */
178 return;
179 }
180
181 IdleConnList *list = (IdleConnList *) data;
182 /* might delete list */
183 if (list && list->remove(conn)) {
184 Comm::ConnectionPointer nonConst = conn;
185 nonConst->close();
186 }
187 }
188
189 void
190 IdleConnList::timeout(int fd, void *data)
191 {
192 debugs(48, 3, "IdleConnList::timeout: FD " << fd);
193 IdleConnList *list = (IdleConnList *) data;
194 Comm::ConnectionPointer temp = new Comm::Connection; // XXX: transition. make timeouts pass conn in
195 temp->fd = fd;
196 if (list->remove(temp)) {
197 temp->close();
198 } else
199 temp->fd = -1; // XXX: transition. prevent temp erasure double-closing FD until timeout CB passess conn in.
200 }
201
202 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
203
204 const char *
205 PconnPool::key(const Comm::ConnectionPointer &destLink, const char *domain)
206 {
207 LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 3 + 10);
208
209 destLink->remote.ToURL(buf, SQUIDHOSTNAMELEN * 3 + 10);
210 if (domain) {
211 int used = strlen(buf);
212 snprintf(buf+used, SQUIDHOSTNAMELEN * 3 + 10-used, "/%s", domain);
213 }
214
215 debugs(48,6,"PconnPool::key(" << destLink << ", " << (domain?domain:"[no domain]") << ") is {" << buf << "}" );
216 return buf;
217 }
218
219 void
220 PconnPool::dumpHist(StoreEntry * e) const
221 {
222 storeAppendPrintf(e,
223 "%s persistent connection counts:\n"
224 "\n"
225 "\treq/\n"
226 "\tconn count\n"
227 "\t---- ---------\n",
228 descr);
229
230 for (int i = 0; i < PCONN_HIST_SZ; i++) {
231 if (hist[i] == 0)
232 continue;
233
234 storeAppendPrintf(e, "\t%4d %9d\n", i, hist[i]);
235 }
236 }
237
238 void
239 PconnPool::dumpHash(StoreEntry *e) const
240 {
241 hash_table *hid = table;
242 hash_first(hid);
243
244 int i = 0;
245 for (hash_link *walker = hid->next; walker; walker = hash_next(hid)) {
246 storeAppendPrintf(e, "\t item %5d: %s\n", i++, (char *)(walker->key));
247 }
248 }
249
250 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
251
252 PconnPool::PconnPool(const char *aDescr) : table(NULL), descr(aDescr)
253 {
254 table = hash_create((HASHCMP *) strcmp, 229, hash_string);
255
256 for (int i = 0; i < PCONN_HIST_SZ; i++)
257 hist[i] = 0;
258
259 PconnModule::GetInstance()->add(this);
260 }
261
262 PconnPool::~PconnPool()
263 {
264 descr = NULL;
265 hashFreeMemory(table);
266 }
267
268 void
269 PconnPool::push(const Comm::ConnectionPointer &conn, const char *domain)
270 {
271 if (fdUsageHigh()) {
272 debugs(48, 3, "PconnPool::push: Not many unused FDs");
273 Comm::ConnectionPointer nonConst = conn;
274 nonConst->close();
275 return;
276 } else if (shutting_down) {
277 Comm::ConnectionPointer nonConst = conn;
278 nonConst->close();
279 debugs(48, 3, "PconnPool::push: Squid is shutting down. Refusing to do anything");
280 return;
281 }
282
283 const char *aKey = key(conn, domain);
284 IdleConnList *list = (IdleConnList *) hash_lookup(table, aKey);
285
286 if (list == NULL) {
287 list = new IdleConnList(aKey, this);
288 debugs(48, 3, "PconnPool::push: new IdleConnList for {" << hashKeyStr(&list->hash) << "}" );
289 hash_join(table, &list->hash);
290 } else {
291 debugs(48, 3, "PconnPool::push: found IdleConnList for {" << hashKeyStr(&list->hash) << "}" );
292 }
293
294 list->push(conn);
295 assert(!comm_has_incomplete_write(conn->fd));
296
297 LOCAL_ARRAY(char, desc, FD_DESC_SZ);
298 snprintf(desc, FD_DESC_SZ, "Idle: %s", aKey);
299 fd_note(conn->fd, desc);
300 debugs(48, 3, HERE << "pushed " << conn << " for " << aKey);
301 }
302
303 bool
304 PconnPool::pop(Comm::ConnectionPointer &destLink, const char *domain, bool isRetriable)
305 {
306 const char * aKey = key(destLink, domain);
307
308 IdleConnList *list = (IdleConnList *)hash_lookup(table, aKey);
309 if (list == NULL) {
310 debugs(48, 3, "PconnPool::pop: lookup for key {" << aKey << "} failed.");
311 return false;
312 } else {
313 debugs(48, 3, "PconnPool::pop: found " << hashKeyStr(&list->hash) << (isRetriable?"(to use)":"(to kill)") );
314 }
315
316 Comm::ConnectionPointer temp = list->findUseable(destLink);
317
318 if (Comm::IsConnOpen(temp)) {
319 list->clearHandlers(temp);
320
321 /* might delete list */
322 if (list->remove(temp) && !isRetriable)
323 temp->close();
324 else
325 destLink = temp;
326 }
327
328 return true;
329 }
330
331 void
332 PconnPool::unlinkList(IdleConnList *list) const
333 {
334 hash_remove_link(table, &list->hash);
335 }
336
337 void
338 PconnPool::count(int uses)
339 {
340 if (uses >= PCONN_HIST_SZ)
341 uses = PCONN_HIST_SZ - 1;
342
343 hist[uses]++;
344 }
345
346 /* ========== PconnModule ============================================ */
347
348 /*
349 * This simple class exists only for the cache manager
350 */
351
352 PconnModule::PconnModule() : pools(NULL), poolCount(0)
353 {
354 pools = (PconnPool **) xcalloc(MAX_NUM_PCONN_POOLS, sizeof(*pools));
355 //TODO: re-link to MemPools. WAS: pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int));
356 debugs(48, 0, "persistent connection module initialized");
357 registerWithCacheManager();
358 }
359
360 PconnModule *
361 PconnModule::GetInstance()
362 {
363 if (instance == NULL)
364 instance = new PconnModule;
365
366 return instance;
367 }
368
369 void
370 PconnModule::registerWithCacheManager(void)
371 {
372 CacheManager::GetInstance()->
373 registerAction("pconn",
374 "Persistent Connection Utilization Histograms",
375 DumpWrapper, 0, 1);
376 }
377
378 void
379
380 PconnModule::add(PconnPool *aPool)
381 {
382 assert(poolCount < MAX_NUM_PCONN_POOLS);
383 *(pools+poolCount) = aPool;
384 poolCount++;
385 }
386
387 void
388 PconnModule::dump(StoreEntry *e)
389 {
390 for (int i = 0; i < poolCount; i++) {
391 storeAppendPrintf(e, "\n Pool %d Stats\n", i);
392 (*(pools+i))->dumpHist(e);
393 storeAppendPrintf(e, "\n Pool %d Hash Table\n",i);
394 (*(pools+i))->dumpHash(e);
395 }
396 }
397
398 void
399 PconnModule::DumpWrapper(StoreEntry *e)
400 {
401 PconnModule::GetInstance()->dump(e);
402 }