]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store.cc
debug levels
[thirdparty/squid.git] / src / store.cc
CommitLineData
164f7660 1
30a4f2a8 2/*
9d66d521 3 * $Id: store.cc,v 1.445 1998/08/18 22:42:21 wessels Exp $
30a4f2a8 4 *
8638fc66 5 * DEBUG: section 20 Storage Manager
30a4f2a8 6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
30a4f2a8 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
c943f331 34 */
090089c4 35
f09f5b26 36#include "squid.h"
090089c4 37
38#define REBUILD_TIMESTAMP_DELTA_MAX 2
227fbb74 39
fcf5283d 40#define STORE_IN_MEM_BUCKETS (229)
090089c4 41
0ee4272b 42const char *memStatusStr[] =
e62d2dea 43{
9dfb6c1c 44 "NOT_IN_MEMORY",
9dfb6c1c 45 "IN_MEMORY"
46};
47
0ee4272b 48const char *pingStatusStr[] =
e62d2dea 49{
f17936ab 50 "PING_NONE",
9dfb6c1c 51 "PING_WAITING",
52 "PING_TIMEOUT",
f17936ab 53 "PING_DONE"
9dfb6c1c 54};
55
0ee4272b 56const char *storeStatusStr[] =
e62d2dea 57{
9dfb6c1c 58 "STORE_OK",
59 "STORE_PENDING",
60 "STORE_ABORTED"
61};
62
0ee4272b 63const char *swapStatusStr[] =
e62d2dea 64{
8350fe9b 65 "SWAPOUT_NONE",
66 "SWAPOUT_OPENING",
67 "SWAPOUT_WRITING",
68 "SWAPOUT_DONE"
9dfb6c1c 69};
70
0a0bf5db 71typedef struct lock_ctrl_t {
582b6456 72 SIH *callback;
0a0bf5db 73 void *callback_data;
74 StoreEntry *e;
75} lock_ctrl_t;
76
e3ef2b09 77/*
78 * local function prototypes
79 */
01fe9bf4 80static int storeCheckExpired(const StoreEntry *);
f5b8bbc4 81static int storeEntryLocked(const StoreEntry *);
82static int storeEntryValidLength(const StoreEntry *);
83static void storeGetMemSpace(int);
b93bcace 84static void storeHashDelete(StoreEntry *);
9fb13bb6 85static MemObject *new_MemObject(const char *, const char *);
6cf028ab 86static void destroy_MemObject(StoreEntry *);
ec878047 87static FREE destroy_StoreEntry;
f5b8bbc4 88static void storePurgeMem(StoreEntry *);
5ad33356 89static unsigned int getKeyCounter(method_t);
8350fe9b 90static int storeKeepInMemory(const StoreEntry *);
a21fbb54 91
e3ef2b09 92/*
93 * local variables
94 */
b93bcace 95static dlink_list inmem_list;
e954773d 96static int store_pages_high = 0;
97static int store_pages_low = 0;
58104eab 98static int store_swap_high = 0;
99static int store_swap_low = 0;
d1497906 100static int store_swap_mid = 0;
e924600d 101static int store_maintain_rate;
66cedb85 102
b8d8561b 103static MemObject *
9fb13bb6 104new_MemObject(const char *url, const char *log_url)
227fbb74 105{
7021844c 106 MemObject *mem = memAllocate(MEM_MEMOBJECT);
cb69b4c7 107 mem->reply = httpReplyCreate();
9fb13bb6 108 mem->url = xstrdup(url);
88738790 109 mem->log_url = xstrdup(log_url);
8350fe9b 110 mem->swapout.fd = -1;
07304bf9 111 mem->object_sz = -1;
ed03a839 112 mem->fd = -1;
59c4d35b 113 /* XXX account log_url */
a3d5953d 114 debug(20, 3) ("new_MemObject: returning %p\n", mem);
30a4f2a8 115 return mem;
227fbb74 116}
117
f09f5b26 118StoreEntry *
9fb13bb6 119new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
090089c4 120{
121 StoreEntry *e = NULL;
7021844c 122 e = memAllocate(MEM_STOREENTRY);
227fbb74 123 if (mem_obj_flag)
9fb13bb6 124 e->mem_obj = new_MemObject(url, log_url);
a3d5953d 125 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
e17dc75c 126 e->expires = e->lastmod = e->lastref = e->timestamp = -1;
227fbb74 127 return e;
090089c4 128}
129
b8d8561b 130static void
0e473d70 131destroy_MemObject(StoreEntry * e)
090089c4 132{
6cf028ab 133 MemObject *mem = e->mem_obj;
123abbe1 134 const Ctx ctx = ctx_enter(mem->url);
a3d5953d 135 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
41f7e38d 136 e->mem_obj = NULL;
9e665466 137 if (!shutting_down)
138 assert(mem->swapout.fd == -1);
18fe65d0 139 stmemFree(&mem->data_hdr);
140 mem->inmem_hi = 0;
59c4d35b 141 /* XXX account log_url */
6cf028ab 142#if USE_ASYNC_IO
0e473d70 143 while (mem->clients != NULL)
6cf028ab 144 storeUnregister(e, mem->clients->callback_data);
145#endif
6982a226 146 /*
147 * There is no way to abort FD-less clients, so they might
148 * still have mem->clients set if mem->fd == -1
149 */
150 assert(mem->fd == -1 || mem->clients == NULL);
cb69b4c7 151 httpReplyDestroy(mem->reply);
30a4f2a8 152 requestUnlink(mem->request);
153 mem->request = NULL;
2ac76861 154 ctx_exit(ctx); /* must exit before we free mem->url */
48959832 155 safe_free(mem->url);
156 safe_free(mem->log_url);
3f6c0fb2 157 memFree(MEM_MEMOBJECT, mem);
090089c4 158}
159
b8d8561b 160static void
ec878047 161destroy_StoreEntry(void *data)
090089c4 162{
ec878047 163 StoreEntry *e = data;
a3d5953d 164 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
9e975e4e 165 assert(e != NULL);
227fbb74 166 if (e->mem_obj)
6cf028ab 167 destroy_MemObject(e);
043b055b 168 storeHashDelete(e);
9fb13bb6 169 assert(e->key == NULL);
8c128028 170 memFree(MEM_STOREENTRY, e);
227fbb74 171}
090089c4 172
090089c4 173/* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
174
f09f5b26 175void
9fb13bb6 176storeHashInsert(StoreEntry * e, const cache_key * key)
090089c4 177{
a3d5953d 178 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
9fb13bb6 179 e, storeKeyText(key));
180 e->key = storeKeyDup(key);
b93bcace 181 hash_join(store_table, (hash_link *) e);
d5bd7c41 182 if (EBIT_TEST(e->flag, KEY_PRIVATE))
48a0f016 183 dlinkAddTail(e, &e->lru, &store_list);
d5bd7c41 184 else
48a0f016 185 dlinkAdd(e, &e->lru, &store_list);
090089c4 186}
187
b93bcace 188static void
b8d8561b 189storeHashDelete(StoreEntry * e)
090089c4 190{
b93bcace 191 hash_remove_link(store_table, (hash_link *) e);
e3ef2b09 192 dlinkDelete(&e->lru, &store_list);
9fb13bb6 193 storeKeyFree(e->key);
194 e->key = NULL;
090089c4 195}
196
090089c4 197/* -------------------------------------------------------------------------- */
198
090089c4 199
200/* get rid of memory copy of the object */
620da955 201/* Only call this if storeCheckPurgeMem(e) returns 1 */
24382924 202static void
b8d8561b 203storePurgeMem(StoreEntry * e)
090089c4 204{
2aca8433 205 if (e->mem_obj == NULL)
090089c4 206 return;
9fb13bb6 207 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
208 storeKeyText(e->key));
090089c4 209 storeSetMemStatus(e, NOT_IN_MEMORY);
6cf028ab 210 destroy_MemObject(e);
06e8899b 211 if (e->swap_status != SWAPOUT_DONE)
8272aded 212 storeRelease(e);
090089c4 213}
214
0a0bf5db 215void
95c4b18f 216storeLockObject(StoreEntry * e)
090089c4 217{
b93bcace 218 if (e->lock_count++ == 0) {
e3ef2b09 219 dlinkDelete(&e->lru, &store_list);
220 dlinkAdd(e, &e->lru, &store_list);
b93bcace 221 }
a3d5953d 222 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
9fb13bb6 223 storeKeyText(e->key), (int) e->lock_count);
b8de7ebe 224 e->lastref = squid_curtime;
090089c4 225}
226
b8d8561b 227void
228storeReleaseRequest(StoreEntry * e)
2285407f 229{
79a15e0a 230 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
58bcd31b 231 return;
8a5e92ce 232 assert(storeEntryLocked(e));
9fb13bb6 233 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e->key));
79a15e0a 234 EBIT_SET(e->flag, RELEASE_REQUEST);
fe54d06d 235 storeSetPrivateKey(e);
2285407f 236}
237
090089c4 238/* unlock object, return -1 if object get released after unlock
239 * otherwise lock_count */
b8d8561b 240int
241storeUnlockObject(StoreEntry * e)
090089c4 242{
6c895381 243 e->lock_count--;
a3d5953d 244 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
9fb13bb6 245 storeKeyText(e->key), e->lock_count);
30a4f2a8 246 if (e->lock_count)
a1e47288 247 return (int) e->lock_count;
5b00be7a 248 if (e->store_status == STORE_PENDING) {
79a15e0a 249 assert(!EBIT_TEST(e->flag, ENTRY_DISPATCHED));
250 EBIT_SET(e->flag, RELEASE_REQUEST);
5b00be7a 251 }
9e975e4e 252 assert(storePendingNClients(e) == 0);
79a15e0a 253 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
30a4f2a8 254 storeRelease(e);
8350fe9b 255 else if (storeKeepInMemory(e)) {
256 storeSetMemStatus(e, IN_MEMORY);
257 requestUnlink(e->mem_obj->request);
258 e->mem_obj->request = NULL;
259 } else
30a4f2a8 260 storePurgeMem(e);
6c895381 261 return 0;
090089c4 262}
263
264/* Lookup an object in the cache.
265 * return just a reference to object, don't start swapping in yet. */
b8d8561b 266StoreEntry *
9fb13bb6 267storeGet(const cache_key * key)
090089c4 268{
9fb13bb6 269 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
270 return (StoreEntry *) hash_lookup(store_table, key);
090089c4 271}
272
88738790 273static unsigned int
5ad33356 274getKeyCounter(method_t method)
04e8dbaa 275{
2d25dc1a 276 static unsigned int key_counter = 0;
7cb386d0 277 if (++key_counter == (1 << 24))
2cc3f720 278 key_counter = 1;
5ad33356 279 return (method << 24) | key_counter;
04e8dbaa 280}
281
6c57e268 282void
b8d8561b 283storeSetPrivateKey(StoreEntry * e)
227fbb74 284{
9fb13bb6 285 const cache_key *newkey;
286 MemObject *mem = e->mem_obj;
bc0bce21 287 if (e->key && EBIT_TEST(e->flag, KEY_PRIVATE))
6eb42cae 288 return; /* is already private */
b109de6b 289 if (e->key) {
290 if (e->swap_file_number > -1)
291 storeDirSwapLog(e, SWAP_LOG_DEL);
bc0bce21 292 storeHashDelete(e);
b109de6b 293 }
9fb13bb6 294 if (mem != NULL) {
5ad33356 295 mem->reqnum = getKeyCounter(mem->method);
2ac237e2 296 newkey = storeKeyPrivate(mem->url, mem->method, mem->reqnum);
9fb13bb6 297 } else {
5ad33356 298 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter(METHOD_NONE));
9fb13bb6 299 }
300 assert(hash_lookup(store_table, newkey) == NULL);
79a15e0a 301 EBIT_SET(e->flag, KEY_PRIVATE);
8638fc66 302 storeHashInsert(e, newkey);
227fbb74 303}
304
b8d8561b 305void
306storeSetPublicKey(StoreEntry * e)
227fbb74 307{
6eb42cae 308 StoreEntry *e2 = NULL;
9fb13bb6 309 const cache_key *newkey;
310 MemObject *mem = e->mem_obj;
bc0bce21 311 if (e->key && !EBIT_TEST(e->flag, KEY_PRIVATE))
6eb42cae 312 return; /* is already public */
9fb13bb6 313 assert(mem);
2ac237e2 314 newkey = storeKeyPublic(mem->url, mem->method);
9fb13bb6 315 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
316 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
07622ccd 317 storeSetPrivateKey(e2);
30a4f2a8 318 storeRelease(e2);
2ac237e2 319 newkey = storeKeyPublic(mem->url, mem->method);
6eb42cae 320 }
bc0bce21 321 if (e->key)
322 storeHashDelete(e);
79a15e0a 323 EBIT_CLR(e->flag, KEY_PRIVATE);
8638fc66 324 storeHashInsert(e, newkey);
b109de6b 325 if (e->swap_file_number > -1)
326 storeDirSwapLog(e, SWAP_LOG_ADD);
227fbb74 327}
328
b8d8561b 329StoreEntry *
88738790 330storeCreateEntry(const char *url, const char *log_url, int flags, method_t method)
090089c4 331{
090089c4 332 StoreEntry *e = NULL;
30a4f2a8 333 MemObject *mem = NULL;
a3d5953d 334 debug(20, 3) ("storeCreateEntry: '%s' icp flags=%x\n", url, flags);
090089c4 335
f09f5b26 336 e = new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ, url, log_url);
30a4f2a8 337 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
338 mem = e->mem_obj;
2ac237e2 339 mem->method = method;
79a15e0a 340 if (neighbors_do_private_keys || !EBIT_TEST(flags, REQ_HIERARCHICAL))
86101e40 341 storeSetPrivateKey(e);
342 else
343 storeSetPublicKey(e);
79a15e0a 344 if (EBIT_TEST(flags, REQ_CACHABLE)) {
345 EBIT_SET(e->flag, ENTRY_CACHABLE);
346 EBIT_CLR(e->flag, RELEASE_REQUEST);
090089c4 347 } else {
79a15e0a 348 EBIT_CLR(e->flag, ENTRY_CACHABLE);
2daae136 349 storeReleaseRequest(e);
090089c4 350 }
234967c9 351 e->store_status = STORE_PENDING;
090089c4 352 storeSetMemStatus(e, NOT_IN_MEMORY);
8350fe9b 353 e->swap_status = SWAPOUT_NONE;
090089c4 354 e->swap_file_number = -1;
090089c4 355 e->refcount = 0;
b8de7ebe 356 e->lastref = squid_curtime;
ca98227c 357 e->timestamp = 0; /* set in storeTimestampsSet() */
30a4f2a8 358 e->ping_status = PING_NONE;
79a15e0a 359 EBIT_SET(e->flag, ENTRY_VALIDATED);
090089c4 360 return e;
361}
362
6eb42cae 363/* Mark object as expired */
b8d8561b 364void
365storeExpireNow(StoreEntry * e)
9174e204 366{
9fb13bb6 367 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e->key));
b8de7ebe 368 e->expires = squid_curtime;
9174e204 369}
370
090089c4 371/* Append incoming data from a primary server to an entry. */
b8d8561b 372void
b8014333 373storeAppend(StoreEntry * e, const char *buf, int len)
090089c4 374{
3a1c3e2f 375 MemObject *mem = e->mem_obj;
376 assert(mem != NULL);
377 assert(len >= 0);
090089c4 378 if (len) {
d0e2935f 379 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
380 len,
381 storeKeyText(e->key));
38792624 382 storeGetMemSpace(len);
18fe65d0 383 stmemAppend(&mem->data_hdr, buf, len);
8350fe9b 384 mem->inmem_hi += len;
090089c4 385 }
d0e2935f 386 if (EBIT_TEST(e->flag, DELAY_SENDING))
387 return;
9d66d521 388#ifdef OPTIMISTIC_IO
389 storeLockObject(e);
390#endif
d0e2935f 391 InvokeHandlers(e);
8350fe9b 392 storeCheckSwapOut(e);
9d66d521 393#ifdef OPTIMISTIC_IO
394 storeUnlockObject(e);
395#endif
090089c4 396}
397
24382924 398#ifdef __STDC__
b8d8561b 399void
fe4e214f 400storeAppendPrintf(StoreEntry * e, const char *fmt,...)
c30c5a73 401{
15c05bb0 402 va_list args;
15c05bb0 403 va_start(args, fmt);
c30c5a73 404#else
b8d8561b 405void
406storeAppendPrintf(va_alist)
15c05bb0 407 va_dcl
408{
409 va_list args;
410 StoreEntry *e = NULL;
0ee4272b 411 const char *fmt = NULL;
15c05bb0 412 va_start(args);
413 e = va_arg(args, StoreEntry *);
414 fmt = va_arg(args, char *);
c30c5a73 415#endif
cb69b4c7 416 storeAppendVPrintf(e, fmt, args);
417 va_end(args);
418}
419
420/* used be storeAppendPrintf and Packer */
421void
422storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
423{
424 LOCAL_ARRAY(char, buf, 4096);
15c05bb0 425 buf[0] = '\0';
cb69b4c7 426 vsnprintf(buf, 4096, fmt, vargs);
15c05bb0 427 storeAppend(e, buf, strlen(buf));
c30c5a73 428}
429
f09f5b26 430int
8350fe9b 431storeCheckCachable(StoreEntry * e)
6602e70e 432{
2ac237e2 433#if CACHE_ALL_METHODS
434 if (e->mem_obj->method != METHOD_GET) {
8350fe9b 435 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
2ac237e2 436 } else
437#endif
438 if (!EBIT_TEST(e->flag, ENTRY_CACHABLE)) {
8350fe9b 439 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
79a15e0a 440 } else if (EBIT_TEST(e->flag, RELEASE_REQUEST)) {
8350fe9b 441 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
b34ed725 442 } else if (e->store_status == STORE_OK && EBIT_TEST(e->flag, ENTRY_BAD_LENGTH)) {
8350fe9b 443 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
79a15e0a 444 } else if (EBIT_TEST(e->flag, ENTRY_NEGCACHED)) {
c0dee43f 445 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
3e98df20 446 return 0; /* avoid release call below */
8350fe9b 447 } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
448 debug(20, 2) ("storeCheckCachable: NO: too big\n");
79a15e0a 449 } else if (EBIT_TEST(e->flag, KEY_PRIVATE)) {
8350fe9b 450 debug(20, 3) ("storeCheckCachable: NO: private key\n");
734b57eb 451 } else if (storeExpiredReferenceAge() < 300) {
c8b09b26 452 debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
453 storeExpiredReferenceAge());
d4432957 454 } else {
6602e70e 455 return 1;
d4432957 456 }
2daae136 457 storeReleaseRequest(e);
79a15e0a 458 EBIT_CLR(e->flag, ENTRY_CACHABLE);
6602e70e 459 return 0;
460}
461
090089c4 462/* Complete transfer into the local cache. */
b8d8561b 463void
464storeComplete(StoreEntry * e)
090089c4 465{
9fb13bb6 466 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->key));
22955fba 467 assert(e->store_status == STORE_PENDING);
07304bf9 468 e->mem_obj->object_sz = e->mem_obj->inmem_hi;
234967c9 469 e->store_status = STORE_OK;
8350fe9b 470 assert(e->mem_status == NOT_IN_MEMORY);
b34ed725 471 if (!storeEntryValidLength(e))
472 EBIT_SET(e->flag, ENTRY_BAD_LENGTH);
6cfa8966 473#if USE_CACHE_DIGESTS
544b1fd4 474 if (e->mem_obj->request)
475 e->mem_obj->request->hier.store_complete_stop = current_time;
39edba21 476#endif
3a1c3e2f 477 InvokeHandlers(e);
8c123b71 478 storeCheckSwapOut(e);
090089c4 479}
480
481/*
474cac1b 482 * Someone wants to abort this transfer. Set the reason in the
483 * request structure, call the server-side callback and mark the
484 * entry for releasing
090089c4 485 */
b8d8561b 486void
9b312a19 487storeAbort(StoreEntry * e, int cbflag)
090089c4 488{
3e98df20 489 MemObject *mem = e->mem_obj;
6801f8a8 490 STABH *callback;
491 void *data;
8f39b81d 492 assert(e->store_status == STORE_PENDING);
493 assert(mem != NULL);
9fb13bb6 494 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->key));
3d02186d 495 storeLockObject(e); /* lock while aborting */
79b5cc5f 496 storeNegativeCache(e);
474cac1b 497 storeReleaseRequest(e);
234967c9 498 e->store_status = STORE_ABORTED;
8350fe9b 499 storeSetMemStatus(e, NOT_IN_MEMORY);
090089c4 500 /* No DISK swap for negative cached object */
8350fe9b 501 e->swap_status = SWAPOUT_NONE;
496e1d76 502 /*
503 * We assign an object length here. The only other place we assign
504 * the object length is in storeComplete()
505 */
07304bf9 506 mem->object_sz = mem->inmem_hi;
474cac1b 507 /* Notify the server side */
508 if (cbflag && mem->abort.callback) {
6801f8a8 509 callback = mem->abort.callback;
510 data = mem->abort.data;
bfcaf585 511 mem->abort.callback = NULL;
6801f8a8 512 mem->abort.data = NULL;
513 callback(data);
bfcaf585 514 }
474cac1b 515 /* Notify the client side */
090089c4 516 InvokeHandlers(e);
6e86c3e8 517 /* Do we need to close the swapout file? */
518 /* Not if we never started swapping out */
6cf028ab 519 /* But we may need to cancel an open/stat in progress if using ASYNC */
520#if USE_ASYNC_IO
521 aioCancel(-1, e);
522#endif
3d02186d 523 if (e->swap_file_number > -1) {
6cf028ab 524#if USE_ASYNC_IO
3d02186d 525 /* Need to cancel any pending ASYNC writes right now */
526 if (mem->swapout.fd >= 0)
527 aioCancel(mem->swapout.fd, NULL);
6cf028ab 528#endif
3d02186d 529 /* we have to close the disk file if there is no write pending */
61038223 530 if (!storeSwapOutWriteQueued(mem))
3d02186d 531 storeSwapOutFileClose(e);
532 }
533 storeUnlockObject(e); /* unlock */
090089c4 534}
535
090089c4 536/* Clear Memory storage to accommodate the given object len */
38792624 537static void
d4432957 538storeGetMemSpace(int size)
090089c4 539{
b32508fb 540 StoreEntry *e = NULL;
20cba4b4 541 int released = 0;
b32508fb 542 static time_t last_check = 0;
e954773d 543 int pages_needed;
b93bcace 544 dlink_node *m;
d6ea85ce 545 dlink_node *head;
79d39a72 546 dlink_node *prev = NULL;
b32508fb 547 if (squid_curtime == last_check)
38792624 548 return;
b32508fb 549 last_check = squid_curtime;
e954773d 550 pages_needed = (size / SM_PAGE_SIZE) + 1;
3f6c0fb2 551 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_high)
38792624 552 return;
311ea387 553 if (store_rebuilding)
38792624 554 return;
a3d5953d 555 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
d6ea85ce 556 head = inmem_list.head;
b93bcace 557 for (m = inmem_list.tail; m; m = prev) {
d6ea85ce 558 if (m == head)
559 break;
8350fe9b 560 prev = m->prev;
b93bcace 561 e = m->data;
d6ea85ce 562 if (storeEntryLocked(e)) {
5999b776 563 dlinkDelete(m, &inmem_list);
564 dlinkAdd(e, m, &inmem_list);
8350fe9b 565 continue;
d6ea85ce 566 }
20cba4b4 567 released++;
2d51a60e 568 storePurgeMem(e);
d6ea85ce 569 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_high)
8350fe9b 570 break;
571 }
5e3d4fef 572 debug(20, 3) ("storeGetMemSpace stats:\n");
59c4d35b 573 debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
20cba4b4 574 debug(20, 3) (" %6d were released\n", released);
090089c4 575}
576
090089c4 577/* The maximum objects to scan for maintain storage space */
fcefe642 578#define MAINTAIN_MAX_SCAN 1024
579#define MAINTAIN_MAX_REMOVE 64
090089c4 580
fcefe642 581/*
582 * This routine is to be called by main loop in main.c.
583 * It removes expired objects on only one bucket for each time called.
584 * returns the number of objects removed
585 *
586 * This should get called 1/s from main().
587 */
679ac4f0 588void
79d39a72 589storeMaintainSwapSpace(void *datanotused)
090089c4 590{
b93bcace 591 dlink_node *m;
79d39a72 592 dlink_node *prev = NULL;
a1b0d7cf 593 StoreEntry *e = NULL;
090089c4 594 int scanned = 0;
090089c4 595 int locked = 0;
679ac4f0 596 int expired = 0;
20cba4b4 597 int max_scan;
598 int max_remove;
ec14f197 599 static time_t last_warn_time = 0;
fcefe642 600 /* We can't delete objects while rebuilding swap */
c8b09b26 601 if (store_rebuilding) {
602 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
679ac4f0 603 return;
d1497906 604 } else if (store_swap_size < store_swap_mid) {
605 max_scan = 100;
606 max_remove = 8;
607 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
c8b09b26 608 } else if (store_swap_size < store_swap_high) {
609 max_scan = 200;
610 max_remove = 8;
611 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 0.1, 1);
20cba4b4 612 } else {
4cb56589 613 max_scan = 500;
c8b09b26 614 max_remove = 32;
615 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 0.0, 1);
20cba4b4 616 }
fcefe642 617 debug(20, 3) ("storeMaintainSwapSpace\n");
e3ef2b09 618 for (m = store_list.tail; m; m = prev) {
b93bcace 619 prev = m->prev;
620 e = m->data;
d150898a 621 scanned++;
b93bcace 622 if (storeEntryLocked(e)) {
ac2197dc 623 /*
624 * If there is a locked entry at the tail of the LRU list,
625 * move it to the beginning to get it out of the way.
626 * Theoretically, we might have all locked objects at the
627 * tail, and then we'll never remove anything here and the
628 * LRU age will go to zero.
629 */
630 if (memInUse(MEM_STOREENTRY) > max_scan) {
4b4cd312 631 dlinkDelete(&e->lru, &store_list);
632 dlinkAdd(e, &e->lru, &store_list);
ac2197dc 633 }
b93bcace 634 locked++;
01fe9bf4 635 } else if (storeCheckExpired(e)) {
b109de6b 636 expired++;
637 storeRelease(e);
090089c4 638 }
d150898a 639 if (expired >= max_remove)
b93bcace 640 break;
d150898a 641 if (scanned >= max_scan)
b93bcace 642 break;
090089c4 643 }
4cb56589 644 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
645 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY));
646 debug(20, 3) (" %6d were scanned\n", scanned);
647 debug(20, 3) (" %6d were locked\n", locked);
648 debug(20, 3) (" %6d were expired\n", expired);
fcefe642 649 if (store_swap_size < Config.Swap.maxSize)
b93bcace 650 return;
651 if (squid_curtime - last_warn_time < 10)
652 return;
653 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
654 store_swap_size, Config.Swap.maxSize);
655 last_warn_time = squid_curtime;
090089c4 656}
657
658
659/* release an object from a cache */
cc61958c 660/* return number of objects released. */
6c78a099 661void
b8d8561b 662storeRelease(StoreEntry * e)
090089c4 663{
9fb13bb6 664 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
090089c4 665 /* If, for any reason we can't discard this object because of an
666 * outstanding request, mark it for pending release */
667 if (storeEntryLocked(e)) {
9174e204 668 storeExpireNow(e);
a3d5953d 669 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
2daae136 670 storeReleaseRequest(e);
6c78a099 671 return;
090089c4 672 }
6cf028ab 673#if USE_ASYNC_IO
25535cbe 674 /*
675 * Make sure all forgotten async ops are cancelled
676 */
677 aioCancel(-1, e);
6cf028ab 678#else
311ea387 679 if (store_rebuilding) {
a3d5953d 680 debug(20, 2) ("storeRelease: Delaying release until store is rebuilt: '%s'\n",
9fb13bb6 681 storeUrl(e));
30a4f2a8 682 storeExpireNow(e);
683 storeSetPrivateKey(e);
79a15e0a 684 EBIT_SET(e->flag, RELEASE_REQUEST);
6c78a099 685 return;
30a4f2a8 686 }
6cf028ab 687#endif
a65ff22e 688 storeLog(STORE_LOG_RELEASE, e);
43fee0b8 689 if (e->swap_file_number > -1) {
25535cbe 690 storeUnlinkFileno(e->swap_file_number);
d961be0a 691 storeDirMapBitReset(e->swap_file_number);
a65ff22e 692 if (e->swap_status == SWAPOUT_DONE)
7b3d6868 693 if (EBIT_TEST(e->flag, ENTRY_VALIDATED))
694 storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1);
b109de6b 695 if (!EBIT_TEST(e->flag, KEY_PRIVATE))
696 storeDirSwapLog(e, SWAP_LOG_DEL);
090089c4 697 }
8350fe9b 698 storeSetMemStatus(e, NOT_IN_MEMORY);
30a4f2a8 699 destroy_StoreEntry(e);
090089c4 700}
701
090089c4 702/* return 1 if a store entry is locked */
24382924 703static int
fe4e214f 704storeEntryLocked(const StoreEntry * e)
090089c4 705{
30a4f2a8 706 if (e->lock_count)
707 return 1;
6cf028ab 708 if (e->swap_status == SWAPOUT_OPENING)
709 return 1;
8350fe9b 710 if (e->swap_status == SWAPOUT_WRITING)
30a4f2a8 711 return 1;
a1e47288 712 if (e->store_status == STORE_PENDING)
713 return 1;
79a15e0a 714 if (EBIT_TEST(e->flag, ENTRY_SPECIAL))
365cb147 715 return 1;
30a4f2a8 716 return 0;
090089c4 717}
718
24382924 719static int
fe4e214f 720storeEntryValidLength(const StoreEntry * e)
6602e70e 721{
ffe4a367 722 int diff;
d8b249ef 723 const HttpReply *reply;
8a5e92ce 724 assert(e->mem_obj != NULL);
07304bf9 725 reply = e->mem_obj->reply;
9fb13bb6 726 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
07304bf9 727 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
728 objectLen(e));
729 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
730 reply->hdr_sz);
731 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
d8b249ef 732 reply->content_length);
733 if (reply->content_length < 0) {
1790d392 734 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
9fb13bb6 735 storeKeyText(e->key));
ffe4a367 736 return 1;
737 }
07304bf9 738 if (reply->hdr_sz == 0) {
1790d392 739 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
9fb13bb6 740 storeKeyText(e->key));
ffe4a367 741 return 1;
742 }
ebf4efff 743 if (e->mem_obj->method == METHOD_HEAD) {
744 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
9fb13bb6 745 storeKeyText(e->key));
ebf4efff 746 return 1;
ffe4a367 747 }
cb69b4c7 748 if (reply->sline.status == HTTP_NOT_MODIFIED)
ebf4efff 749 return 1;
cb69b4c7 750 if (reply->sline.status == HTTP_NO_CONTENT)
ebf4efff 751 return 1;
d8b249ef 752 diff = reply->hdr_sz + reply->content_length - objectLen(e);
ebf4efff 753 if (diff == 0)
754 return 1;
755 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
756 diff < 0 ? -diff : diff,
757 diff < 0 ? "small" : "big",
758 storeKeyText(e->key));
759 return 0;
ffe4a367 760}
6602e70e 761
66cedb85 762static void
763storeInitHashValues(void)
764{
765 int i;
a7e59001 766 /* Calculate size of hash table (maximum currently 64k buckets). */
38792624 767 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
86101e40 768 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
66cedb85 769 Config.Swap.maxSize, i);
38792624 770 i /= Config.Store.objectsPerBucket;
a3d5953d 771 debug(20, 1) ("Target number of buckets: %d\n", i);
66cedb85 772 /* ideally the full scan period should be configurable, for the
773 * moment it remains at approximately 24 hours. */
9fb13bb6 774 store_hash_buckets = storeKeyHashBuckets(i);
24ffafb4 775 store_maintain_rate = 86400 / store_hash_buckets;
9fb13bb6 776 assert(store_maintain_rate > 0);
7454a5c2 777 debug(20, 1) ("Using %d Store buckets, replacement runs every %d second%s\n",
9fb13bb6 778 store_hash_buckets,
66cedb85 779 store_maintain_rate,
780 store_maintain_rate == 1 ? null_string : "s");
a47b9029 781 debug(20, 1) ("Max Mem size: %d KB\n", Config.Mem.maxSize >> 10);
782 debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
66cedb85 783}
784
b8d8561b 785void
786storeInit(void)
c943f331 787{
25535cbe 788 storeKeyInit();
66cedb85 789 storeInitHashValues();
9fb13bb6 790 store_table = hash_create(storeKeyHashCmp,
791 store_hash_buckets, storeKeyHashHash);
8638fc66 792 storeDigestInit();
e3ef2b09 793 storeLogOpen();
85407535 794 if (storeVerifyCacheDirs() < 0) {
d377699f 795 xstrncpy(tmp_error_buf,
796 "\tFailed to verify one of the swap directories, Check cache.log\n"
797 "\tfor details. Run 'squid -z' to create swap directories\n"
798 "\tif needed, or if running Squid for the first time.",
799 ERROR_BUF_SZ);
800 fatal(tmp_error_buf);
801 }
e3ef2b09 802 storeDirOpenSwapLogs();
e3ef2b09 803 store_list.head = store_list.tail = NULL;
b93bcace 804 inmem_list.head = inmem_list.tail = NULL;
5830cdb3 805 storeRebuildStart();
4cb56589 806 cachemgrRegister("storedir",
22f3fd98 807 "Store Directory Stats",
1da3b90b 808 storeDirStats, 0, 1);
b1c0cc67 809}
c943f331 810
b8d8561b 811void
812storeConfigure(void)
b1c0cc67 813{
e954773d 814 int store_mem_high = 0;
815 int store_mem_low = 0;
b6f794d6 816 store_mem_high = (long) (Config.Mem.maxSize / 100) *
817 Config.Mem.highWaterMark;
818 store_mem_low = (long) (Config.Mem.maxSize / 100) *
819 Config.Mem.lowWaterMark;
090089c4 820
b1c0cc67 821 store_swap_high = (long) (((float) Config.Swap.maxSize *
822 (float) Config.Swap.highWaterMark) / (float) 100);
823 store_swap_low = (long) (((float) Config.Swap.maxSize *
824 (float) Config.Swap.lowWaterMark) / (float) 100);
d1497906 825 store_swap_mid = (store_swap_high >> 1) + (store_swap_low >> 1);
e954773d 826
827 store_pages_high = store_mem_high / SM_PAGE_SIZE;
828 store_pages_low = store_mem_low / SM_PAGE_SIZE;
090089c4 829}
830
56f29785 831static int
8350fe9b 832storeKeepInMemory(const StoreEntry * e)
56f29785 833{
8350fe9b 834 MemObject *mem = e->mem_obj;
835 if (mem == NULL)
56f29785 836 return 0;
18fe65d0 837 if (mem->data_hdr.head == NULL)
8350fe9b 838 return 0;
839 return mem->inmem_lo == 0;
56f29785 840}
841
b8d8561b 842static int
01fe9bf4 843storeCheckExpired(const StoreEntry * e)
620da955 844{
845 if (storeEntryLocked(e))
846 return 0;
1148b77c 847 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
848 return 1;
79a15e0a 849 if (EBIT_TEST(e->flag, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
7cc1830f 850 return 1;
fcefe642 851 if (squid_curtime - e->lastref > storeExpiredReferenceAge())
66cedb85 852 return 1;
66cedb85 853 return 0;
620da955 854}
855
db9ccd5a 856/*
857 * storeExpiredReferenceAge
858 *
429fdbec 859 * The LRU age is scaled exponentially between 1 minute and
860 * Config.referenceAge , when store_swap_low < store_swap_size <
861 * store_swap_high. This keeps store_swap_size within the low and high
862 * water marks. If the cache is very busy then store_swap_size stays
863 * closer to the low water mark, if it is not busy, then it will stay
864 * near the high water mark. The LRU age value can be examined on the
865 * cachemgr 'info' page.
fbdfb3cf 866 */
35feb4aa 867time_t
58104eab 868storeExpiredReferenceAge(void)
869{
58104eab 870 double x;
871 double z;
872 time_t age;
429fdbec 873 x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
874 x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
8ac49cdc 875 z = pow((double) (Config.referenceAge / 60), x);
58104eab 876 age = (time_t) (z * 60.0);
877 if (age < 60)
878 age = 60;
879 else if (age > 31536000)
880 age = 31536000;
881 return age;
0a362c34 882}
2edc2504 883
b8d8561b 884void
885storeNegativeCache(StoreEntry * e)
79b5cc5f 886{
ff5731b1 887 e->expires = squid_curtime + Config.negativeTtl;
79a15e0a 888 EBIT_SET(e->flag, ENTRY_NEGCACHED);
79b5cc5f 889}
0a21bd84 890
891void
892storeFreeMemory(void)
893{
ec878047 894 hashFreeItems(store_table, destroy_StoreEntry);
0a21bd84 895 hashFreeMemory(store_table);
afe95a7e 896 store_table = NULL;
8638fc66 897 if (store_digest)
898 cacheDigestDestroy(store_digest);
899 store_digest = NULL;
0a21bd84 900}
a7e59001 901
902int
903expiresMoreThan(time_t expires, time_t when)
904{
48f44632 905 if (expires < 0) /* No Expires given */
906 return 1;
907 return (expires > (squid_curtime + when));
a7e59001 908}
fe54d06d 909
910int
911storeEntryValidToSend(StoreEntry * e)
912{
79a15e0a 913 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
fe54d06d 914 return 0;
79a15e0a 915 if (EBIT_TEST(e->flag, ENTRY_NEGCACHED))
44f78c24 916 if (e->expires <= squid_curtime)
fe54d06d 917 return 0;
918 if (e->store_status == STORE_ABORTED)
919 return 0;
920 return 1;
921}
62663274 922
ca98227c 923void
924storeTimestampsSet(StoreEntry * entry)
925{
926 time_t served_date = -1;
2246b732 927 const HttpReply *reply = entry->mem_obj->reply;
d8b249ef 928 served_date = reply->date;
cb69b4c7 929 if (served_date < 0)
930 served_date = squid_curtime;
d8b249ef 931 entry->expires = reply->expires;
1c3e77cd 932 entry->lastmod = reply->last_modified;
ca98227c 933 entry->timestamp = served_date;
934}
429fdbec 935
bfcaf585 936void
937storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
938{
939 MemObject *mem = e->mem_obj;
940 assert(mem);
941 assert(mem->abort.callback == NULL);
942 mem->abort.callback = cb;
943 mem->abort.data = data;
944}
945
946void
947storeUnregisterAbort(StoreEntry * e)
948{
949 MemObject *mem = e->mem_obj;
950 assert(mem);
951 mem->abort.callback = NULL;
952}
88738790 953
954void
955storeMemObjectDump(MemObject * mem)
956{
18fe65d0 957 debug(20, 1) ("MemObject->data.head: %p\n",
958 mem->data_hdr.head);
959 debug(20, 1) ("MemObject->data.tail: %p\n",
960 mem->data_hdr.tail);
961 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
962 mem->data_hdr.origin_offset);
88738790 963 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
5f6ac48b 964 (int) mem->start_ping.tv_sec,
965 (int) mem->start_ping.tv_usec);
8350fe9b 966 debug(20, 1) ("MemObject->inmem_hi: %d\n",
5f6ac48b 967 (int) mem->inmem_hi);
8350fe9b 968 debug(20, 1) ("MemObject->inmem_lo: %d\n",
5f6ac48b 969 (int) mem->inmem_lo);
88738790 970 debug(20, 1) ("MemObject->clients: %p\n",
971 mem->clients);
972 debug(20, 1) ("MemObject->nclients: %d\n",
973 mem->nclients);
8350fe9b 974 debug(20, 1) ("MemObject->swapout.fd: %d\n",
975 mem->swapout.fd);
88738790 976 debug(20, 1) ("MemObject->reply: %p\n",
977 mem->reply);
978 debug(20, 1) ("MemObject->request: %p\n",
979 mem->request);
980 debug(20, 1) ("MemObject->log_url: %p %s\n",
981 mem->log_url,
982 checkNullString(mem->log_url));
983}
8350fe9b 984
f09f5b26 985void
e3ef2b09 986storeEntryDump(StoreEntry * e, int l)
d377699f 987{
e3ef2b09 988 debug(20, l) ("StoreEntry->key: %s\n", storeKeyText(e->key));
989 debug(20, l) ("StoreEntry->next: %p\n", e->next);
990 debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
991 debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
992 debug(20, l) ("StoreEntry->lastref: %d\n", (int) e->lastref);
993 debug(20, l) ("StoreEntry->expires: %d\n", (int) e->expires);
994 debug(20, l) ("StoreEntry->lastmod: %d\n", (int) e->lastmod);
07304bf9 995 debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
e3ef2b09 996 debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
997 debug(20, l) ("StoreEntry->flag: %X\n", e->flag);
998 debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number);
999 debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
1000 debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
1001 debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
1002 debug(20, l) ("StoreEntry->store_status: %d\n", (int) e->store_status);
1003 debug(20, l) ("StoreEntry->swap_status: %d\n", (int) e->swap_status);
d377699f 1004}
1005
8350fe9b 1006/* NOTE, this function assumes only two mem states */
f09f5b26 1007void
8350fe9b 1008storeSetMemStatus(StoreEntry * e, int new_status)
1009{
b93bcace 1010 MemObject *mem = e->mem_obj;
8350fe9b 1011 if (new_status == e->mem_status)
1012 return;
b93bcace 1013 assert(mem != NULL);
1014 if (new_status == IN_MEMORY) {
1015 assert(mem->inmem_lo == 0);
2ac237e2 1016 dlinkAdd(e, &mem->lru, &inmem_list);
59c4d35b 1017 hot_obj_count++;
b93bcace 1018 } else {
2ac237e2 1019 dlinkDelete(&mem->lru, &inmem_list);
59c4d35b 1020 hot_obj_count--;
b93bcace 1021 }
8350fe9b 1022 e->mem_status = new_status;
1023}
6e86c3e8 1024
9fb13bb6 1025const char *
1026storeUrl(const StoreEntry * e)
1027{
1028 if (e == NULL)
24ffafb4 1029 return "[null_entry]";
9fb13bb6 1030 else if (e->mem_obj == NULL)
24ffafb4 1031 return "[null_mem_obj]";
9fb13bb6 1032 else
1033 return e->mem_obj->url;
1034}
24ffafb4 1035
1036void
1037storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
1038{
1039 if (e->mem_obj)
1040 return;
1041 e->mem_obj = new_MemObject(url, log_url);
1042}
64763c37 1043
438fc1e3 1044/* this just sets DELAY_SENDING */
1045void
8daca701 1046storeBuffer(StoreEntry * e)
438fc1e3 1047{
8daca701 1048 EBIT_SET(e->flag, DELAY_SENDING);
438fc1e3 1049}
1050
1051/* this just clears DELAY_SENDING and Invokes the handlers */
1052void
8daca701 1053storeBufferFlush(StoreEntry * e)
438fc1e3 1054{
8daca701 1055 EBIT_CLR(e->flag, DELAY_SENDING);
1056 InvokeHandlers(e);
d5bd7c41 1057 storeCheckSwapOut(e);
438fc1e3 1058}
25535cbe 1059
1060void
1061storeUnlinkFileno(int fileno)
1062{
07304bf9 1063 debug(20, 5) ("storeUnlinkFileno: %08X\n", fileno);
25535cbe 1064#if USE_ASYNC_IO
1065 safeunlink(storeSwapFullPath(fileno, NULL), 1);
1066#else
1067 unlinkdUnlink(storeSwapFullPath(fileno, NULL));
1068#endif
1069}
07304bf9 1070
1071int
1072objectLen(const StoreEntry * e)
1073{
1074 assert(e->mem_obj != NULL);
1075 return e->mem_obj->object_sz;
1076}
1077
1078int
1079contentLen(const StoreEntry * e)
1080{
1081 assert(e->mem_obj != NULL);
1082 assert(e->mem_obj->reply != NULL);
1083 return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
1084}
f3986a15 1085
1086HttpReply *
1087storeEntryReply(StoreEntry * e)
1088{
1089 if (NULL == e)
3d02186d 1090 return NULL;
f3986a15 1091 if (NULL == e->mem_obj)
3d02186d 1092 return NULL;
f3986a15 1093 return e->mem_obj->reply;
1094}