3
* $Id: MemObject.cc,v 1.24 2006/09/20 00:59:26 adrian Exp $
5
* DEBUG: section 19 Store Memory Primitives
6
* AUTHOR: Robert Collins
8
* SQUID Web Proxy Cache http://www.squid-cache.org/
9
* ----------------------------------------------------------
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.
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.
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.
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.
37
#include "MemObject.h"
38
#include "HttpRequest.h"
39
#include "HttpReply.h"
41
#include "StoreClient.h"
44
#include "DelayPools.h"
48
/* TODO: make this global or private */
49
#if URL_CHECKSUM_DEBUG
50
static unsigned int url_checksum(const char *url);
52
url_checksum(const char *url)
56
static unsigned char digest[16];
58
MD5Update(&M, (unsigned char *) url, strlen(url));
60
xmemcpy(&ck, digest, sizeof(ck));
66
RemovalPolicy * mem_policy = NULL;
69
MemObject::inUseCount()
71
return Pool().inUseCount();
74
MemObject::MemObject(char const *aUrl, char const *aLog_url)
76
debugs(20, 3, HERE << "new MemObject " << this);
77
HttpReply *rep = new HttpReply;
79
_reply = HTTPMSGLOCK(rep);
82
#if URL_CHECKSUM_DEBUG
84
chksum = url_checksum(url);
88
log_url = xstrdup(aLog_url);
92
/* XXX account log_url */
95
MemObject::~MemObject()
97
debugs(20, 3, HERE << "del MemObject " << this);
98
const Ctx ctx = ctx_enter(url);
99
#if URL_CHECKSUM_DEBUG
101
assert(chksum == url_checksum(url));
105
assert(swapout.sio == NULL);
107
data_hdr.freeContent();
111
* There is no way to abort FD-less clients, so they might
112
* still have mem->clients set.
114
assert(clients.head == NULL);
118
HTTPMSGUNLOCK(_reply);
120
HTTPMSGUNLOCK(request);
122
ctx_exit(ctx); /* must exit before we free mem->url */
126
safe_free(log_url); /* XXX account log_url */
128
safe_free(vary_headers);
132
MemObject::unlinkRequest()
134
HTTPMSGUNLOCK(request);
138
MemObject::write ( StoreIOBuffer writeBuffer, STMCB *callback, void *callbackData)
140
PROF_start(MemObject_write);
141
debug(19, 6) ("memWrite: offset %lu len %ld\n", (unsigned long)writeBuffer.offset, (long)writeBuffer.length);
143
/* the offset is into the content, not the headers */
144
writeBuffer.offset += (_reply ? _reply->hdr_sz : 0);
146
/* We don't separate out mime headers yet, so ensure that the first
147
* write is at offset 0 - where they start
149
assert (data_hdr.endOffset() || writeBuffer.offset == 0);
151
assert (data_hdr.write (writeBuffer));
152
callback (callbackData, writeBuffer);
153
PROF_stop(MemObject_write);
157
MemObject::dump() const
161
/* do we want this one? */
162
debug(20, 1) ("MemObject->data.origin_offset: %d\n",
163
data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0);
166
debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
167
(int) start_ping.tv_sec,
168
(int) start_ping.tv_usec);
169
debug(20, 1) ("MemObject->inmem_hi: %d\n",
170
(int) data_hdr.endOffset());
171
debug(20, 1) ("MemObject->inmem_lo: %d\n",
173
debug(20, 1) ("MemObject->nclients: %d\n",
175
debug(20, 1) ("MemObject->reply: %p\n",
177
debug(20, 1) ("MemObject->request: %p\n",
179
debug(20, 1) ("MemObject->log_url: %p %s\n",
181
checkNullString(log_url));
185
MemObject::getReply() const
191
MemObject::replaceHttpReply(HttpReply *newrep)
193
HTTPMSGUNLOCK(_reply);
194
_reply = HTTPMSGLOCK(newrep);
197
struct LowestMemReader : public unary_function<store_client, void>
199
LowestMemReader(off_t seed):current(seed){}
201
void operator() (store_client const &x)
203
if (x.memReaderHasLowerOffset(current))
204
current = x.copyInto.offset;
210
struct StoreClientStats : public unary_function<store_client, void>
212
StoreClientStats(MemBuf *anEntry):where(anEntry),index(0){}
214
void operator()(store_client const &x)
216
x.dumpStats(where, index++);
224
MemObject::stat (MemBuf * mb) const
226
mb->Printf("\t%s %s\n",
227
RequestMethodStr[method], log_url);
228
mb->Printf("\tinmem_lo: %d\n", (int) inmem_lo);
229
mb->Printf("\tinmem_hi: %d\n", (int) data_hdr.endOffset());
230
mb->Printf("\tswapout: %d bytes queued\n",
231
(int) swapout.queue_offset);
233
if (swapout.sio.getRaw())
234
mb->Printf("\tswapout: %d bytes written\n",
235
(int) swapout.sio->offset());
237
StoreClientStats statsVisitor(mb);
239
for_each<StoreClientStats>(clients, statsVisitor);
243
MemObject::endOffset () const
245
return data_hdr.endOffset();
249
MemObject::size() const
260
assert(swapout.sio == NULL);
261
data_hdr.freeContent();
263
/* Should we check for clients? */
268
MemObject::lowestMemReaderOffset() const
270
LowestMemReader lowest (endOffset() + 1);
272
for_each <LowestMemReader>(clients, lowest);
274
return lowest.current;
277
/* XXX: This is wrong. It breaks *badly* on range combining */
279
MemObject::readAheadPolicyCanRead() const
281
return (size_t)endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + (Config.readAheadGap << 10);
285
MemObject::addClient(store_client *aClient)
288
dlinkAdd(aClient, &aClient->node, &clients);
291
#if URL_CHECKSUM_DEBUG
293
MemObject::checkUrlChecksum () const
295
assert(chksum == url_checksum(url));
301
* How much of the object data is on the disk?
304
MemObject::objectBytesOnDisk() const
307
* NOTE: storeOffset() represents the disk file size,
308
* not the amount of object data on disk.
310
* If we don't have at least 'swap_hdr_sz' bytes
311
* then none of the object data is on disk.
313
* This should still be safe if swap_hdr_sz == 0,
314
* meaning we haven't even opened the swapout file
318
if (swapout.sio.getRaw() == NULL)
321
off_t nwritten = swapout.sio->offset();
323
if (nwritten <= (off_t)swap_hdr_sz)
326
return (size_t) (nwritten - swap_hdr_sz);
330
MemObject::policyLowestOffsetToKeep() const
333
* Careful. lowest_offset can be greater than endOffset(), such
334
* as in the case of a range request.
336
off_t lowest_offset = lowestMemReaderOffset();
338
if (endOffset() < lowest_offset ||
339
endOffset() - inmem_lo > (ssize_t)Config.Store.maxInMemObjSize)
340
return lowest_offset;
346
MemObject::trimSwappable()
348
off_t new_mem_lo = policyLowestOffsetToKeep();
350
* We should only free up to what we know has been written
351
* to disk, not what has been queued for writing. Otherwise
352
* there will be a chunk of the data which is not in memory
353
* and is not yet on disk.
354
* The -1 makes sure the page isn't freed until storeSwapOut has
355
* walked to the next page. (mem->swapout.memnode)
359
if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo)
360
new_mem_lo = on_disk - 1;
362
if (new_mem_lo == -1)
363
new_mem_lo = 0; /* the above might become -1 */
365
data_hdr.freeDataUpto(new_mem_lo);
367
inmem_lo = new_mem_lo;
371
MemObject::trimUnSwappable()
373
off_t new_mem_lo = policyLowestOffsetToKeep();
374
assert (new_mem_lo > 0);
376
data_hdr.freeDataUpto(new_mem_lo);
377
inmem_lo = new_mem_lo;
382
MemObject::isContiguous() const
384
bool result = data_hdr.hasContigousContentRange (Range<size_t>(inmem_lo, endOffset()));
385
/* XXX : make this higher level */
386
debug (19, result ? 4 :3) ("MemObject::isContiguous: Returning %s\n",
387
result ? "true" : "false");
392
MemObject::mostBytesWanted(int max) const
395
/* identify delay id with largest allowance */
396
DelayId largestAllowance = mostBytesAllowed ();
397
return largestAllowance.bytesWanted(0, max);
405
MemObject::setNoDelay(bool const newValue)
409
for (dlink_node *node = clients.head; node; node = node->next) {
410
store_client *sc = (store_client *) node->data;
411
sc->delayId.setNoDelay(newValue);
418
MemObject::delayRead(DeferredRead const &aRead)
420
deferredReads.delayRead(aRead);
424
MemObject::kickReads()
426
deferredReads.kickReads(-1);
431
MemObject::mostBytesAllowed() const
437
for (dlink_node *node = clients.head; node; node = node->next) {
438
store_client *sc = (store_client *) node->data;
440
/* This test is invalid because the client may be writing data
441
* and thus will want data immediately.
442
* If we include the test, there is a race condition when too much
443
* data is read - if all sc's are writing when a read is scheduled.
447
if (!sc->callbackPending())
448
/* not waiting for more data */
453
if (sc->getType() != STORE_MEM_CLIENT)
454
/* reading off disk */
457
j = sc->delayId.bytesWanted(0, sc->copyInto.length);
461
result = sc->delayId;