]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemObject.cc
Make client attaching while an object is being swapped in disk clients
[thirdparty/squid.git] / src / MemObject.cc
CommitLineData
528b2c61 1
2/*
262a0e14 3 * $Id$
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.
26ac0430 24 *
528b2c61 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.
26ac0430 29 *
528b2c61 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;
c3031d67 55 SquidMD5_CTX M;
528b2c61 56 static unsigned char digest[16];
c3031d67 57 SquidMD5Init(&M);
58 SquidMD5Update(&M, (unsigned char *) url, strlen(url));
59 SquidMD5Final(digest, &M);
528b2c61 60 xmemcpy(&ck, digest, sizeof(ck));
61 return ck;
62}
62e76326 63
528b2c61 64#endif
65
aa839030 66RemovalPolicy * mem_policy = NULL;
67
528b2c61 68size_t
69MemObject::inUseCount()
70{
9f9e06f3 71 return Pool().inUseCount();
528b2c61 72}
73
06a5ae20 74MemObject::MemObject(char const *aUrl, char const *aLog_url)
528b2c61 75{
e85137f1 76 debugs(20, 3, HERE << "new MemObject " << this);
4a56ee8d 77 HttpReply *rep = new HttpReply;
78
6dd9f4bd 79 _reply = HTTPMSGLOCK(rep);
528b2c61 80 url = xstrdup(aUrl);
4a56ee8d 81
528b2c61 82#if URL_CHECKSUM_DEBUG
62e76326 83
528b2c61 84 chksum = url_checksum(url);
4a56ee8d 85
528b2c61 86#endif
62e76326 87
528b2c61 88 log_url = xstrdup(aLog_url);
4a56ee8d 89
528b2c61 90 object_sz = -1;
4a56ee8d 91
528b2c61 92 /* XXX account log_url */
528b2c61 93}
94
95MemObject::~MemObject()
96{
e85137f1 97 debugs(20, 3, HERE << "del MemObject " << this);
528b2c61 98 const Ctx ctx = ctx_enter(url);
528b2c61 99#if URL_CHECKSUM_DEBUG
62e76326 100
528b2c61 101 assert(chksum == url_checksum(url));
102#endif
62e76326 103
528b2c61 104 if (!shutting_down)
105 assert(swapout.sio == NULL);
62e76326 106
528b2c61 107 data_hdr.freeContent();
62e76326 108
9cdee68d 109#if 0
528b2c61 110 /*
111 * There is no way to abort FD-less clients, so they might
9cdee68d 112 * still have mem->clients set.
528b2c61 113 */
9cdee68d 114 assert(clients.head == NULL);
115
116#endif
62e76326 117
6dd9f4bd 118 HTTPMSGUNLOCK(_reply);
62e76326 119
6dd9f4bd 120 HTTPMSGUNLOCK(request);
62e76326 121
528b2c61 122 ctx_exit(ctx); /* must exit before we free mem->url */
62e76326 123
528b2c61 124 safe_free(url);
62e76326 125
528b2c61 126 safe_free(log_url); /* XXX account log_url */
62e76326 127
528b2c61 128 safe_free(vary_headers);
129}
130
131void
132MemObject::unlinkRequest()
133{
6dd9f4bd 134 HTTPMSGUNLOCK(request);
528b2c61 135}
136
137void
138MemObject::write ( StoreIOBuffer writeBuffer, STMCB *callback, void *callbackData)
139{
1d5161bd 140 PROF_start(MemObject_write);
4a7a3d56 141 debugs(19, 6, "memWrite: offset " << writeBuffer.offset << " len " << writeBuffer.length);
528b2c61 142
143 /* the offset is into the content, not the headers */
144 writeBuffer.offset += (_reply ? _reply->hdr_sz : 0);
145
146 /* We don't separate out mime headers yet, so ensure that the first
26ac0430 147 * write is at offset 0 - where they start
528b2c61 148 */
149 assert (data_hdr.endOffset() || writeBuffer.offset == 0);
150
151 assert (data_hdr.write (writeBuffer));
152 callback (callbackData, writeBuffer);
1d5161bd 153 PROF_stop(MemObject_write);
528b2c61 154}
155
156void
157MemObject::dump() const
158{
42a503bd 159 data_hdr.dump();
528b2c61 160#if 0
161 /* do we want this one? */
bf8fe701 162 debugs(20, 1, "MemObject->data.origin_offset: " << (data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0));
528b2c61 163#endif
62e76326 164
4a7a3d56 165 debugs(20, 1, "MemObject->start_ping: " << start_ping.tv_sec << "."<< std::setfill('0') << std::setw(6) << start_ping.tv_usec);
166 debugs(20, 1, "MemObject->inmem_hi: " << data_hdr.endOffset());
167 debugs(20, 1, "MemObject->inmem_lo: " << inmem_lo);
bf8fe701 168 debugs(20, 1, "MemObject->nclients: " << nclients);
169 debugs(20, 1, "MemObject->reply: " << _reply);
170 debugs(20, 1, "MemObject->request: " << request);
30abd221 171 debugs(20, 1, "MemObject->log_url: " << log_url << " " << checkNullString(log_url));
528b2c61 172}
173
174HttpReply const *
175MemObject::getReply() const
176{
177 return _reply;
178}
179
4a56ee8d 180void
181MemObject::replaceHttpReply(HttpReply *newrep)
182{
6dd9f4bd 183 HTTPMSGUNLOCK(_reply);
184 _reply = HTTPMSGLOCK(newrep);
4a56ee8d 185}
186
26ac0430
AJ
187struct LowestMemReader : public unary_function<store_client, void> {
188 LowestMemReader(int64_t seed):current(seed) {}
62e76326 189
26ac0430 190 void operator() (store_client const &x) {
62e76326 191 if (x.memReaderHasLowerOffset(current))
192 current = x.copyInto.offset;
193 }
194
47f6e231 195 int64_t current;
528b2c61 196};
197
26ac0430
AJ
198struct StoreClientStats : public unary_function<store_client, void> {
199 StoreClientStats(MemBuf *anEntry):where(anEntry),index(0) {}
62e76326 200
26ac0430 201 void operator()(store_client const &x) {
62e76326 202 x.dumpStats(where, index++);
528b2c61 203 }
62e76326 204
fcc35180 205 MemBuf *where;
528b2c61 206 size_t index;
207};
208
209void
fcc35180 210MemObject::stat (MemBuf * mb) const
528b2c61 211{
2fe7eff9 212 mb->Printf("\t%s %s\n",
60745f24 213 RequestMethodStr(method), log_url);
47f6e231 214 mb->Printf("\tinmem_lo: %"PRId64"\n", inmem_lo);
215 mb->Printf("\tinmem_hi: %"PRId64"\n", data_hdr.endOffset());
216 mb->Printf("\tswapout: %"PRId64" bytes queued\n",
217 swapout.queue_offset);
62e76326 218
528b2c61 219 if (swapout.sio.getRaw())
47f6e231 220 mb->Printf("\tswapout: %"PRId64" bytes written\n",
221 (int64_t) swapout.sio->offset());
62e76326 222
fcc35180 223 StoreClientStats statsVisitor(mb);
62e76326 224
4cbb7fa8 225 for_each<StoreClientStats>(clients, statsVisitor);
528b2c61 226}
227
47f6e231 228int64_t
528b2c61 229MemObject::endOffset () const
230{
231 return data_hdr.endOffset();
232}
233
47f6e231 234int64_t
528b2c61 235MemObject::size() const
236{
62e76326 237 if (object_sz < 0)
238 return endOffset();
239
528b2c61 240 return object_sz;
241}
242
243void
244MemObject::reset()
245{
246 assert(swapout.sio == NULL);
247 data_hdr.freeContent();
248 inmem_lo = 0;
249 /* Should we check for clients? */
250}
251
252
47f6e231 253int64_t
528b2c61 254MemObject::lowestMemReaderOffset() const
255{
256 LowestMemReader lowest (endOffset() + 1);
257
4cbb7fa8 258 for_each <LowestMemReader>(clients, lowest);
62e76326 259
528b2c61 260 return lowest.current;
261}
262
263/* XXX: This is wrong. It breaks *badly* on range combining */
264bool
265MemObject::readAheadPolicyCanRead() const
266{
47f6e231 267 return endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + Config.readAheadGap;
528b2c61 268}
269
270void
271MemObject::addClient(store_client *aClient)
272{
273 ++nclients;
274 dlinkAdd(aClient, &aClient->node, &clients);
275}
276
277#if URL_CHECKSUM_DEBUG
278void
279MemObject::checkUrlChecksum () const
280{
281 assert(chksum == url_checksum(url));
282}
62e76326 283
528b2c61 284#endif
285
286/*
287 * How much of the object data is on the disk?
288 */
47f6e231 289int64_t
528b2c61 290MemObject::objectBytesOnDisk() const
291{
292 /*
293 * NOTE: storeOffset() represents the disk file size,
294 * not the amount of object data on disk.
26ac0430 295 *
528b2c61 296 * If we don't have at least 'swap_hdr_sz' bytes
297 * then none of the object data is on disk.
298 *
299 * This should still be safe if swap_hdr_sz == 0,
300 * meaning we haven't even opened the swapout file
301 * yet.
302 */
62e76326 303
528b2c61 304 if (swapout.sio.getRaw() == NULL)
62e76326 305 return 0;
306
47f6e231 307 int64_t nwritten = swapout.sio->offset();
62e76326 308
ed013b6c 309 if (nwritten <= (int64_t)swap_hdr_sz)
62e76326 310 return 0;
311
47f6e231 312 return (nwritten - swap_hdr_sz);
528b2c61 313}
314
47f6e231 315int64_t
528b2c61 316MemObject::policyLowestOffsetToKeep() const
317{
318 /*
319 * Careful. lowest_offset can be greater than endOffset(), such
320 * as in the case of a range request.
321 */
47f6e231 322 int64_t lowest_offset = lowestMemReaderOffset();
62e76326 323
528b2c61 324 if (endOffset() < lowest_offset ||
ed013b6c 325 endOffset() - inmem_lo > (int64_t)Config.Store.maxInMemObjSize)
62e76326 326 return lowest_offset;
327
528b2c61 328 return inmem_lo;
329}
330
331void
332MemObject::trimSwappable()
333{
47f6e231 334 int64_t new_mem_lo = policyLowestOffsetToKeep();
528b2c61 335 /*
336 * We should only free up to what we know has been written
337 * to disk, not what has been queued for writing. Otherwise
338 * there will be a chunk of the data which is not in memory
339 * and is not yet on disk.
340 * The -1 makes sure the page isn't freed until storeSwapOut has
341 * walked to the next page. (mem->swapout.memnode)
342 */
47f6e231 343 int64_t on_disk;
62e76326 344
528b2c61 345 if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo)
62e76326 346 new_mem_lo = on_disk - 1;
347
528b2c61 348 if (new_mem_lo == -1)
62e76326 349 new_mem_lo = 0; /* the above might become -1 */
350
528b2c61 351 data_hdr.freeDataUpto(new_mem_lo);
62e76326 352
528b2c61 353 inmem_lo = new_mem_lo;
354}
355
356void
357MemObject::trimUnSwappable()
358{
47f6e231 359 int64_t new_mem_lo = policyLowestOffsetToKeep();
528b2c61 360 assert (new_mem_lo > 0);
361
362 data_hdr.freeDataUpto(new_mem_lo);
363 inmem_lo = new_mem_lo;
364}
365
366
367bool
368MemObject::isContiguous() const
369{
47f6e231 370 bool result = data_hdr.hasContigousContentRange (Range<int64_t>(inmem_lo, endOffset()));
528b2c61 371 /* XXX : make this higher level */
bf8fe701 372 debugs (19, result ? 4 :3, "MemObject::isContiguous: Returning " << (result ? "true" : "false"));
528b2c61 373 return result;
374}
b67e2c8c 375
376int
377MemObject::mostBytesWanted(int max) const
378{
379#if DELAY_POOLS
b67e2c8c 380 /* identify delay id with largest allowance */
381 DelayId largestAllowance = mostBytesAllowed ();
382 return largestAllowance.bytesWanted(0, max);
383#else
62e76326 384
b67e2c8c 385 return max;
386#endif
387}
388
a46d2c0e 389void
390MemObject::setNoDelay(bool const newValue)
391{
392#if DELAY_POOLS
393
394 for (dlink_node *node = clients.head; node; node = node->next) {
395 store_client *sc = (store_client *) node->data;
396 sc->delayId.setNoDelay(newValue);
397 }
398
399#endif
400}
401
402void
403MemObject::delayRead(DeferredRead const &aRead)
404{
405 deferredReads.delayRead(aRead);
406}
407
408void
409MemObject::kickReads()
410{
411 deferredReads.kickReads(-1);
412}
413
b67e2c8c 414#if DELAY_POOLS
415DelayId
416MemObject::mostBytesAllowed() const
417{
418 int j;
419 int jmax = -1;
420 DelayId result;
62e76326 421
b67e2c8c 422 for (dlink_node *node = clients.head; node; node = node->next) {
62e76326 423 store_client *sc = (store_client *) node->data;
d576a6a6 424#if 0
62e76326 425 /* This test is invalid because the client may be writing data
426 * and thus will want data immediately.
427 * If we include the test, there is a race condition when too much
428 * data is read - if all sc's are writing when a read is scheduled.
429 * XXX: fixme.
430 */
431
432 if (!sc->callbackPending())
433 /* not waiting for more data */
434 continue;
435
d576a6a6 436#endif
62e76326 437
438 if (sc->getType() != STORE_MEM_CLIENT)
439 /* reading off disk */
440 continue;
441
442 j = sc->delayId.bytesWanted(0, sc->copyInto.length);
443
444 if (j > jmax) {
445 jmax = j;
446 result = sc->delayId;
447 }
b67e2c8c 448 }
62e76326 449
b67e2c8c 450 return result;
451}
62e76326 452
b67e2c8c 453#endif