]>
Commit | Line | Data |
---|---|---|
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 | |
50 | static unsigned int url_checksum(const char *url); | |
51 | unsigned int | |
52 | url_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 | 66 | size_t |
67 | MemObject::inUseCount() | |
68 | { | |
528b2c61 | 69 | MemPoolStats stats; |
62e76326 | 70 | |
b001e822 | 71 | Pool().getStats (&stats); |
62e76326 | 72 | |
528b2c61 | 73 | return stats.items_inuse; |
74 | } | |
75 | ||
76 | MemObject::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 | ||
91 | MemObject::~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 | ||
130 | void | |
131 | MemObject::unlinkRequest() | |
132 | { | |
133 | /* XXX Should we assert(request)? */ | |
134 | requestUnlink(request); | |
135 | request = NULL; | |
136 | } | |
137 | ||
138 | void | |
139 | MemObject::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 | ||
157 | void | |
158 | MemObject::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 | ||
185 | HttpReply const * | |
186 | MemObject::getReply() const | |
187 | { | |
188 | return _reply; | |
189 | } | |
190 | ||
191 | struct 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 | ||
204 | struct 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 | ||
217 | void | |
fcc35180 | 218 | MemObject::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 | ||
236 | off_t | |
237 | MemObject::endOffset () const | |
238 | { | |
239 | return data_hdr.endOffset(); | |
240 | } | |
241 | ||
242 | size_t | |
243 | MemObject::size() const | |
244 | { | |
62e76326 | 245 | if (object_sz < 0) |
246 | return endOffset(); | |
247 | ||
528b2c61 | 248 | return object_sz; |
249 | } | |
250 | ||
251 | void | |
252 | MemObject::reset() | |
253 | { | |
254 | assert(swapout.sio == NULL); | |
255 | data_hdr.freeContent(); | |
256 | inmem_lo = 0; | |
257 | /* Should we check for clients? */ | |
258 | } | |
259 | ||
260 | ||
261 | off_t | |
262 | MemObject::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 */ | |
272 | bool | |
273 | MemObject::readAheadPolicyCanRead() const | |
274 | { | |
88dc45c6 | 275 | return (size_t)endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + (Config.readAheadGap << 10); |
528b2c61 | 276 | } |
277 | ||
278 | void | |
279 | MemObject::addClient(store_client *aClient) | |
280 | { | |
281 | ++nclients; | |
282 | dlinkAdd(aClient, &aClient->node, &clients); | |
283 | } | |
284 | ||
285 | #if URL_CHECKSUM_DEBUG | |
286 | void | |
287 | MemObject::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 | */ | |
297 | size_t | |
298 | MemObject::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 | ||
323 | off_t | |
324 | MemObject::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 | ||
339 | void | |
340 | MemObject::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 | ||
364 | void | |
365 | MemObject::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 | ||
375 | bool | |
376 | MemObject::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 | |
385 | int | |
386 | MemObject::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 | 398 | void |
399 | MemObject::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 | ||
411 | void | |
412 | MemObject::delayRead(DeferredRead const &aRead) | |
413 | { | |
414 | deferredReads.delayRead(aRead); | |
415 | } | |
416 | ||
417 | void | |
418 | MemObject::kickReads() | |
419 | { | |
420 | deferredReads.kickReads(-1); | |
421 | } | |
422 | ||
b67e2c8c | 423 | #if DELAY_POOLS |
424 | DelayId | |
425 | MemObject::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 |