]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemObject.cc
Add MemBuf to MemPools. This would have made it easier to find
[thirdparty/squid.git] / src / MemObject.cc
CommitLineData
528b2c61 1
2/*
0eb49b6d 3 * $Id: MemObject.cc,v 1.16 2005/09/17 04:53:44 wessels Exp $
528b2c61 4 *
5 * DEBUG: section 19 Store Memory Primitives
6 * AUTHOR: Robert Collins
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36#include "squid.h"
37#include "MemObject.h"
38#include "HttpRequest.h"
39#include "HttpReply.h"
40#include "Store.h"
41#include "StoreClient.h"
42#include "Generic.h"
b67e2c8c 43#if DELAY_POOLS
44#include "DelayPools.h"
45#endif
0eb49b6d 46#include "MemBuf.h"
528b2c61 47
48/* TODO: make this global or private */
49#if URL_CHECKSUM_DEBUG
50static unsigned int url_checksum(const char *url);
51unsigned int
52url_checksum(const char *url)
53{
54 unsigned int ck;
55 MD5_CTX M;
56 static unsigned char digest[16];
57 MD5Init(&M);
58 MD5Update(&M, (unsigned char *) url, strlen(url));
59 MD5Final(digest, &M);
60 xmemcpy(&ck, digest, sizeof(ck));
61 return ck;
62}
62e76326 63
528b2c61 64#endif
65
528b2c61 66size_t
67MemObject::inUseCount()
68{
528b2c61 69 MemPoolStats stats;
62e76326 70
b001e822 71 Pool().getStats (&stats);
62e76326 72
528b2c61 73 return stats.items_inuse;
74}
75
76MemObject::MemObject(char const *aUrl, char const *aLog_url) :
62e76326 77 _reply (httpReplyCreate())
528b2c61 78{
79 url = xstrdup(aUrl);
80#if URL_CHECKSUM_DEBUG
62e76326 81
528b2c61 82 chksum = url_checksum(url);
83#endif
62e76326 84
528b2c61 85 log_url = xstrdup(aLog_url);
86 object_sz = -1;
528b2c61 87 /* XXX account log_url */
88 debug(20, 3) ("MemObject::MemObject: initialized %p\n", this);
89}
90
91MemObject::~MemObject()
92{
93 const Ctx ctx = ctx_enter(url);
94 debug(20, 3) ("destroy_MemObject: destroying %p\n", this);
95#if URL_CHECKSUM_DEBUG
62e76326 96
528b2c61 97 assert(chksum == url_checksum(url));
98#endif
62e76326 99
528b2c61 100 if (!shutting_down)
101 assert(swapout.sio == NULL);
62e76326 102
528b2c61 103 data_hdr.freeContent();
62e76326 104
9cdee68d 105#if 0
528b2c61 106 /*
107 * There is no way to abort FD-less clients, so they might
9cdee68d 108 * still have mem->clients set.
528b2c61 109 */
9cdee68d 110 assert(clients.head == NULL);
111
112#endif
62e76326 113
c8f4eac4 114 if (_reply)
115 httpReplyDestroy((HttpReply *)_reply);
62e76326 116
528b2c61 117 requestUnlink(request);
62e76326 118
528b2c61 119 request = NULL;
62e76326 120
528b2c61 121 ctx_exit(ctx); /* must exit before we free mem->url */
62e76326 122
528b2c61 123 safe_free(url);
62e76326 124
528b2c61 125 safe_free(log_url); /* XXX account log_url */
62e76326 126
528b2c61 127 safe_free(vary_headers);
128}
129
130void
131MemObject::unlinkRequest()
132{
133 /* XXX Should we assert(request)? */
134 requestUnlink(request);
135 request = NULL;
136}
137
138void
139MemObject::write ( StoreIOBuffer writeBuffer, STMCB *callback, void *callbackData)
140{
1d5161bd 141 PROF_start(MemObject_write);
23da259f 142 debug(19, 6) ("memWrite: offset %lu len %ld\n", (unsigned long)writeBuffer.offset, (long)writeBuffer.length);
528b2c61 143
144 /* the offset is into the content, not the headers */
145 writeBuffer.offset += (_reply ? _reply->hdr_sz : 0);
146
147 /* We don't separate out mime headers yet, so ensure that the first
148 * write is at offset 0 - where they start
149 */
150 assert (data_hdr.endOffset() || writeBuffer.offset == 0);
151
152 assert (data_hdr.write (writeBuffer));
153 callback (callbackData, writeBuffer);
1d5161bd 154 PROF_stop(MemObject_write);
528b2c61 155}
156
157void
158MemObject::dump() const
159{
42a503bd 160 data_hdr.dump();
528b2c61 161#if 0
162 /* do we want this one? */
163 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
62e76326 164 data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0);
528b2c61 165#endif
62e76326 166
528b2c61 167 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
62e76326 168 (int) start_ping.tv_sec,
169 (int) start_ping.tv_usec);
528b2c61 170 debug(20, 1) ("MemObject->inmem_hi: %d\n",
62e76326 171 (int) data_hdr.endOffset());
528b2c61 172 debug(20, 1) ("MemObject->inmem_lo: %d\n",
62e76326 173 (int) inmem_lo);
528b2c61 174 debug(20, 1) ("MemObject->nclients: %d\n",
62e76326 175 nclients);
528b2c61 176 debug(20, 1) ("MemObject->reply: %p\n",
62e76326 177 _reply);
528b2c61 178 debug(20, 1) ("MemObject->request: %p\n",
62e76326 179 request);
528b2c61 180 debug(20, 1) ("MemObject->log_url: %p %s\n",
62e76326 181 log_url,
182 checkNullString(log_url));
528b2c61 183}
184
185HttpReply const *
186MemObject::getReply() const
187{
188 return _reply;
189}
190
191struct LowestMemReader : public unary_function<store_client, void>
192{
193 LowestMemReader(off_t seed):current(seed){}
62e76326 194
195 void operator() (store_client const &x)
196 {
197 if (x.memReaderHasLowerOffset(current))
198 current = x.copyInto.offset;
199 }
200
528b2c61 201 off_t current;
202};
203
204struct StoreClientStats : public unary_function<store_client, void>
205{
fcc35180 206 StoreClientStats(MemBuf *anEntry):where(anEntry),index(0){}
62e76326 207
208 void operator()(store_client const &x)
209 {
210 x.dumpStats(where, index++);
528b2c61 211 }
62e76326 212
fcc35180 213 MemBuf *where;
528b2c61 214 size_t index;
215};
216
217void
fcc35180 218MemObject::stat (MemBuf * mb) const
528b2c61 219{
fcc35180 220 memBufPrintf(mb, "\t%s %s\n",
221 RequestMethodStr[method], log_url);
222 memBufPrintf(mb, "\tinmem_lo: %d\n", (int) inmem_lo);
223 memBufPrintf(mb, "\tinmem_hi: %d\n", (int) data_hdr.endOffset());
224 memBufPrintf(mb, "\tswapout: %d bytes queued\n",
225 (int) swapout.queue_offset);
62e76326 226
528b2c61 227 if (swapout.sio.getRaw())
fcc35180 228 memBufPrintf(mb, "\tswapout: %d bytes written\n",
229 (int) swapout.sio->offset());
62e76326 230
fcc35180 231 StoreClientStats statsVisitor(mb);
62e76326 232
4cbb7fa8 233 for_each<StoreClientStats>(clients, statsVisitor);
528b2c61 234}
235
236off_t
237MemObject::endOffset () const
238{
239 return data_hdr.endOffset();
240}
241
242size_t
243MemObject::size() const
244{
62e76326 245 if (object_sz < 0)
246 return endOffset();
247
528b2c61 248 return object_sz;
249}
250
251void
252MemObject::reset()
253{
254 assert(swapout.sio == NULL);
255 data_hdr.freeContent();
256 inmem_lo = 0;
257 /* Should we check for clients? */
258}
259
260
261off_t
262MemObject::lowestMemReaderOffset() const
263{
264 LowestMemReader lowest (endOffset() + 1);
265
4cbb7fa8 266 for_each <LowestMemReader>(clients, lowest);
62e76326 267
528b2c61 268 return lowest.current;
269}
270
271/* XXX: This is wrong. It breaks *badly* on range combining */
272bool
273MemObject::readAheadPolicyCanRead() const
274{
88dc45c6 275 return (size_t)endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + (Config.readAheadGap << 10);
528b2c61 276}
277
278void
279MemObject::addClient(store_client *aClient)
280{
281 ++nclients;
282 dlinkAdd(aClient, &aClient->node, &clients);
283}
284
285#if URL_CHECKSUM_DEBUG
286void
287MemObject::checkUrlChecksum () const
288{
289 assert(chksum == url_checksum(url));
290}
62e76326 291
528b2c61 292#endif
293
294/*
295 * How much of the object data is on the disk?
296 */
297size_t
298MemObject::objectBytesOnDisk() const
299{
300 /*
301 * NOTE: storeOffset() represents the disk file size,
302 * not the amount of object data on disk.
303 *
304 * If we don't have at least 'swap_hdr_sz' bytes
305 * then none of the object data is on disk.
306 *
307 * This should still be safe if swap_hdr_sz == 0,
308 * meaning we haven't even opened the swapout file
309 * yet.
310 */
62e76326 311
528b2c61 312 if (swapout.sio.getRaw() == NULL)
62e76326 313 return 0;
314
528b2c61 315 off_t nwritten = swapout.sio->offset();
62e76326 316
528b2c61 317 if (nwritten <= (off_t)swap_hdr_sz)
62e76326 318 return 0;
319
528b2c61 320 return (size_t) (nwritten - swap_hdr_sz);
321}
322
323off_t
324MemObject::policyLowestOffsetToKeep() const
325{
326 /*
327 * Careful. lowest_offset can be greater than endOffset(), such
328 * as in the case of a range request.
329 */
330 off_t lowest_offset = lowestMemReaderOffset();
62e76326 331
528b2c61 332 if (endOffset() < lowest_offset ||
62e76326 333 endOffset() - inmem_lo > (ssize_t)Config.Store.maxInMemObjSize)
334 return lowest_offset;
335
528b2c61 336 return inmem_lo;
337}
338
339void
340MemObject::trimSwappable()
341{
342 off_t new_mem_lo = policyLowestOffsetToKeep();
343 /*
344 * We should only free up to what we know has been written
345 * to disk, not what has been queued for writing. Otherwise
346 * there will be a chunk of the data which is not in memory
347 * and is not yet on disk.
348 * The -1 makes sure the page isn't freed until storeSwapOut has
349 * walked to the next page. (mem->swapout.memnode)
350 */
351 off_t on_disk;
62e76326 352
528b2c61 353 if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo)
62e76326 354 new_mem_lo = on_disk - 1;
355
528b2c61 356 if (new_mem_lo == -1)
62e76326 357 new_mem_lo = 0; /* the above might become -1 */
358
528b2c61 359 data_hdr.freeDataUpto(new_mem_lo);
62e76326 360
528b2c61 361 inmem_lo = new_mem_lo;
362}
363
364void
365MemObject::trimUnSwappable()
366{
367 off_t new_mem_lo = policyLowestOffsetToKeep();
368 assert (new_mem_lo > 0);
369
370 data_hdr.freeDataUpto(new_mem_lo);
371 inmem_lo = new_mem_lo;
372}
373
374
375bool
376MemObject::isContiguous() const
377{
4c50505b 378 bool result = data_hdr.hasContigousContentRange (Range<size_t>(inmem_lo, endOffset()));
528b2c61 379 /* XXX : make this higher level */
a46d2c0e 380 debug (19, result ? 4 :3) ("MemObject::isContiguous: Returning %s\n",
381 result ? "true" : "false");
528b2c61 382 return result;
383}
b67e2c8c 384
385int
386MemObject::mostBytesWanted(int max) const
387{
388#if DELAY_POOLS
b67e2c8c 389 /* identify delay id with largest allowance */
390 DelayId largestAllowance = mostBytesAllowed ();
391 return largestAllowance.bytesWanted(0, max);
392#else
62e76326 393
b67e2c8c 394 return max;
395#endif
396}
397
a46d2c0e 398void
399MemObject::setNoDelay(bool const newValue)
400{
401#if DELAY_POOLS
402
403 for (dlink_node *node = clients.head; node; node = node->next) {
404 store_client *sc = (store_client *) node->data;
405 sc->delayId.setNoDelay(newValue);
406 }
407
408#endif
409}
410
411void
412MemObject::delayRead(DeferredRead const &aRead)
413{
414 deferredReads.delayRead(aRead);
415}
416
417void
418MemObject::kickReads()
419{
420 deferredReads.kickReads(-1);
421}
422
b67e2c8c 423#if DELAY_POOLS
424DelayId
425MemObject::mostBytesAllowed() const
426{
427 int j;
428 int jmax = -1;
429 DelayId result;
62e76326 430
b67e2c8c 431 for (dlink_node *node = clients.head; node; node = node->next) {
62e76326 432 store_client *sc = (store_client *) node->data;
d576a6a6 433#if 0
62e76326 434 /* This test is invalid because the client may be writing data
435 * and thus will want data immediately.
436 * If we include the test, there is a race condition when too much
437 * data is read - if all sc's are writing when a read is scheduled.
438 * XXX: fixme.
439 */
440
441 if (!sc->callbackPending())
442 /* not waiting for more data */
443 continue;
444
d576a6a6 445#endif
62e76326 446
447 if (sc->getType() != STORE_MEM_CLIENT)
448 /* reading off disk */
449 continue;
450
451 j = sc->delayId.bytesWanted(0, sc->copyInto.length);
452
453 if (j > jmax) {
454 jmax = j;
455 result = sc->delayId;
456 }
b67e2c8c 457 }
62e76326 458
b67e2c8c 459 return result;
460}
62e76326 461
b67e2c8c 462#endif