]>
Commit | Line | Data |
---|---|---|
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 | |
50 | static unsigned int url_checksum(const char *url); | |
51 | unsigned int | |
52 | url_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 | 66 | RemovalPolicy * mem_policy = NULL; |
67 | ||
528b2c61 | 68 | size_t |
69 | MemObject::inUseCount() | |
70 | { | |
9f9e06f3 | 71 | return Pool().inUseCount(); |
528b2c61 | 72 | } |
73 | ||
06a5ae20 | 74 | MemObject::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 | ||
95 | MemObject::~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 | ||
131 | void | |
132 | MemObject::unlinkRequest() | |
133 | { | |
6dd9f4bd | 134 | HTTPMSGUNLOCK(request); |
528b2c61 | 135 | } |
136 | ||
137 | void | |
138 | MemObject::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 | ||
156 | void | |
157 | MemObject::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 | ||
174 | HttpReply const * | |
175 | MemObject::getReply() const | |
176 | { | |
177 | return _reply; | |
178 | } | |
179 | ||
4a56ee8d | 180 | void |
181 | MemObject::replaceHttpReply(HttpReply *newrep) | |
182 | { | |
6dd9f4bd | 183 | HTTPMSGUNLOCK(_reply); |
184 | _reply = HTTPMSGLOCK(newrep); | |
4a56ee8d | 185 | } |
186 | ||
26ac0430 AJ |
187 | struct 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 |
198 | struct 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 | ||
209 | void | |
fcc35180 | 210 | MemObject::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 | 228 | int64_t |
528b2c61 | 229 | MemObject::endOffset () const |
230 | { | |
231 | return data_hdr.endOffset(); | |
232 | } | |
233 | ||
47f6e231 | 234 | int64_t |
528b2c61 | 235 | MemObject::size() const |
236 | { | |
62e76326 | 237 | if (object_sz < 0) |
238 | return endOffset(); | |
239 | ||
528b2c61 | 240 | return object_sz; |
241 | } | |
242 | ||
243 | void | |
244 | MemObject::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 | 253 | int64_t |
528b2c61 | 254 | MemObject::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 */ | |
264 | bool | |
265 | MemObject::readAheadPolicyCanRead() const | |
266 | { | |
47f6e231 | 267 | return endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + Config.readAheadGap; |
528b2c61 | 268 | } |
269 | ||
270 | void | |
271 | MemObject::addClient(store_client *aClient) | |
272 | { | |
273 | ++nclients; | |
274 | dlinkAdd(aClient, &aClient->node, &clients); | |
275 | } | |
276 | ||
277 | #if URL_CHECKSUM_DEBUG | |
278 | void | |
279 | MemObject::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 | 289 | int64_t |
528b2c61 | 290 | MemObject::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 | 315 | int64_t |
528b2c61 | 316 | MemObject::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 | ||
331 | void | |
332 | MemObject::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 | ||
356 | void | |
357 | MemObject::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 | ||
367 | bool | |
368 | MemObject::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 | |
376 | int | |
377 | MemObject::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 | 389 | void |
390 | MemObject::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 | ||
402 | void | |
403 | MemObject::delayRead(DeferredRead const &aRead) | |
404 | { | |
405 | deferredReads.delayRead(aRead); | |
406 | } | |
407 | ||
408 | void | |
409 | MemObject::kickReads() | |
410 | { | |
411 | deferredReads.kickReads(-1); | |
412 | } | |
413 | ||
b67e2c8c | 414 | #if DELAY_POOLS |
415 | DelayId | |
416 | MemObject::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 |