]> git.ipfire.org Git - thirdparty/squid.git/blob - src/MemObject.cc
Import of fix-ranges branch
[thirdparty/squid.git] / src / MemObject.cc
1
2 /*
3 * $Id: MemObject.cc,v 1.1 2003/01/23 00:37:14 robertc Exp $
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"
43
44 /* TODO: make this global or private */
45 #if URL_CHECKSUM_DEBUG
46 static unsigned int url_checksum(const char *url);
47 unsigned int
48 url_checksum(const char *url)
49 {
50 unsigned int ck;
51 MD5_CTX M;
52 static unsigned char digest[16];
53 MD5Init(&M);
54 MD5Update(&M, (unsigned char *) url, strlen(url));
55 MD5Final(digest, &M);
56 xmemcpy(&ck, digest, sizeof(ck));
57 return ck;
58 }
59 #endif
60
61 MemPool *MemObject::pool = NULL;
62
63 void *
64 MemObject::operator new (size_t byteCount)
65 {
66 /* derived classes with different sizes must implement their own new */
67 assert (byteCount == sizeof (MemObject));
68 if (!pool)
69 pool = memPoolCreate("MemObject", sizeof (MemObject));
70 return memPoolAlloc(pool);
71 }
72
73 void
74 MemObject::operator delete (void *address)
75 {
76 memPoolFree(pool, address);
77 }
78
79 size_t
80 MemObject::inUseCount()
81 {
82 if (!pool)
83 return 0;
84 MemPoolStats stats;
85 memPoolGetStats (&stats, pool);
86 return stats.items_inuse;
87 }
88
89 MemObject::MemObject(char const *aUrl, char const *aLog_url) :
90 _reply (httpReplyCreate())
91 {
92 url = xstrdup(aUrl);
93 #if URL_CHECKSUM_DEBUG
94 chksum = url_checksum(url);
95 #endif
96 log_url = xstrdup(aLog_url);
97 object_sz = -1;
98 fd = -1;
99 /* XXX account log_url */
100 debug(20, 3) ("MemObject::MemObject: initialized %p\n", this);
101 }
102
103 MemObject::~MemObject()
104 {
105 const Ctx ctx = ctx_enter(url);
106 debug(20, 3) ("destroy_MemObject: destroying %p\n", this);
107 #if URL_CHECKSUM_DEBUG
108 assert(chksum == url_checksum(url));
109 #endif
110 if (!shutting_down)
111 assert(swapout.sio == NULL);
112 data_hdr.freeContent();
113 /*
114 * There is no way to abort FD-less clients, so they might
115 * still have mem->clients set if mem->fd == -1
116 */
117 assert(fd == -1 || clients.head == NULL);
118 httpReplyDestroy((HttpReply *)_reply);
119 requestUnlink(request);
120 request = NULL;
121 ctx_exit(ctx); /* must exit before we free mem->url */
122 safe_free(url);
123 safe_free(log_url); /* XXX account log_url */
124 safe_free(vary_headers);
125 }
126
127 void
128 MemObject::unlinkRequest()
129 {
130 /* XXX Should we assert(request)? */
131 requestUnlink(request);
132 request = NULL;
133 }
134
135 void
136 MemObject::write ( StoreIOBuffer writeBuffer, STMCB *callback, void *callbackData)
137 {
138 debug(19, 6) ("memWrite: offset %lu len %d\n", writeBuffer.offset, writeBuffer.length);
139
140 /* the offset is into the content, not the headers */
141 writeBuffer.offset += (_reply ? _reply->hdr_sz : 0);
142
143 /* We don't separate out mime headers yet, so ensure that the first
144 * write is at offset 0 - where they start
145 */
146 assert (data_hdr.endOffset() || writeBuffer.offset == 0);
147
148 assert (data_hdr.write (writeBuffer));
149 callback (callbackData, writeBuffer);
150 }
151
152 void
153 MemObject::dump() const
154 {
155 debug(20, 1) ("MemObject->data.head: %p\n",
156 data_hdr.head);
157 debug(20, 1) ("MemObject->data.tail: %p\n",
158 data_hdr.tail);
159 #if 0
160 /* do we want this one? */
161 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
162 data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0);
163 #endif
164 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
165 (int) start_ping.tv_sec,
166 (int) start_ping.tv_usec);
167 debug(20, 1) ("MemObject->inmem_hi: %d\n",
168 (int) data_hdr.endOffset());
169 debug(20, 1) ("MemObject->inmem_lo: %d\n",
170 (int) inmem_lo);
171 debug(20, 1) ("MemObject->nclients: %d\n",
172 nclients);
173 debug(20, 1) ("MemObject->reply: %p\n",
174 _reply);
175 debug(20, 1) ("MemObject->request: %p\n",
176 request);
177 debug(20, 1) ("MemObject->log_url: %p %s\n",
178 log_url,
179 checkNullString(log_url));
180 }
181
182 HttpReply const *
183 MemObject::getReply() const
184 {
185 return _reply;
186 }
187
188 struct LowestMemReader : public unary_function<store_client, void>
189 {
190 LowestMemReader(off_t seed):current(seed){}
191 void operator() (store_client const &x)
192 {
193 if (x.memReaderHasLowerOffset(current))
194 current = x.copyInto.offset; }
195 off_t current;
196 };
197
198 struct StoreClientStats : public unary_function<store_client, void>
199 {
200 StoreClientStats(StoreEntry *anEntry):where(anEntry),index(0){}
201 void operator()(store_client const &x) {
202 x.dumpStats(where, index++);
203 }
204 StoreEntry *where;
205 size_t index;
206 };
207
208 void
209 MemObject::stat (StoreEntry *s) const
210 {
211 storeAppendPrintf(s, "\t%s %s\n",
212 RequestMethodStr[method], log_url);
213 storeAppendPrintf(s, "\tinmem_lo: %d\n", (int) inmem_lo);
214 storeAppendPrintf(s, "\tinmem_hi: %d\n", (int) data_hdr.endOffset());
215 storeAppendPrintf(s, "\tswapout: %d bytes queued\n",
216 (int) swapout.queue_offset);
217 if (swapout.sio.getRaw())
218 storeAppendPrintf(s, "\tswapout: %d bytes written\n",
219 (int) swapout.sio->offset());
220 StoreClientStats statsVisitor(s);
221 for_each(clients, statsVisitor);
222 }
223
224 off_t
225 MemObject::endOffset () const
226 {
227 return data_hdr.endOffset();
228 }
229
230 size_t
231 MemObject::size() const
232 {
233 if (object_sz < 0)
234 return endOffset();
235 return object_sz;
236 }
237
238 void
239 MemObject::reset()
240 {
241 assert(swapout.sio == NULL);
242 data_hdr.freeContent();
243 inmem_lo = 0;
244 /* Should we check for clients? */
245 }
246
247
248 off_t
249 MemObject::lowestMemReaderOffset() const
250 {
251 LowestMemReader lowest (endOffset() + 1);
252
253 for_each (clients, lowest);
254
255 return lowest.current;
256 }
257
258 /* XXX: This is wrong. It breaks *badly* on range combining */
259 bool
260 MemObject::readAheadPolicyCanRead() const
261 {
262 return (size_t)endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + Config.readAheadGap;
263 }
264
265 void
266 MemObject::addClient(store_client *aClient)
267 {
268 ++nclients;
269 dlinkAdd(aClient, &aClient->node, &clients);
270 }
271
272 #if URL_CHECKSUM_DEBUG
273 void
274 MemObject::checkUrlChecksum () const
275 {
276 assert(chksum == url_checksum(url));
277 }
278 #endif
279
280 /*
281 * How much of the object data is on the disk?
282 */
283 size_t
284 MemObject::objectBytesOnDisk() const
285 {
286 /*
287 * NOTE: storeOffset() represents the disk file size,
288 * not the amount of object data on disk.
289 *
290 * If we don't have at least 'swap_hdr_sz' bytes
291 * then none of the object data is on disk.
292 *
293 * This should still be safe if swap_hdr_sz == 0,
294 * meaning we haven't even opened the swapout file
295 * yet.
296 */
297 if (swapout.sio.getRaw() == NULL)
298 return 0;
299 off_t nwritten = swapout.sio->offset();
300 if (nwritten <= (off_t)swap_hdr_sz)
301 return 0;
302 return (size_t) (nwritten - swap_hdr_sz);
303 }
304
305 off_t
306 MemObject::policyLowestOffsetToKeep() const
307 {
308 /*
309 * Careful. lowest_offset can be greater than endOffset(), such
310 * as in the case of a range request.
311 */
312 off_t lowest_offset = lowestMemReaderOffset();
313 if (endOffset() < lowest_offset ||
314 endOffset() - inmem_lo > (ssize_t)Config.Store.maxInMemObjSize)
315 return lowest_offset;
316
317 return inmem_lo;
318 }
319
320 void
321 MemObject::trimSwappable()
322 {
323 off_t new_mem_lo = policyLowestOffsetToKeep();
324 /*
325 * We should only free up to what we know has been written
326 * to disk, not what has been queued for writing. Otherwise
327 * there will be a chunk of the data which is not in memory
328 * and is not yet on disk.
329 * The -1 makes sure the page isn't freed until storeSwapOut has
330 * walked to the next page. (mem->swapout.memnode)
331 */
332 off_t on_disk;
333 if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo)
334 new_mem_lo = on_disk - 1;
335 if (new_mem_lo == -1)
336 new_mem_lo = 0; /* the above might become -1 */
337 data_hdr.freeDataUpto(new_mem_lo);
338 inmem_lo = new_mem_lo;
339 }
340
341 void
342 MemObject::trimUnSwappable()
343 {
344 off_t new_mem_lo = policyLowestOffsetToKeep();
345 assert (new_mem_lo > 0);
346
347 data_hdr.freeDataUpto(new_mem_lo);
348 inmem_lo = new_mem_lo;
349 }
350
351
352 bool
353 MemObject::isContiguous() const
354 {
355 bool result = data_hdr.hasContigousContentRange (inmem_lo, endOffset());
356 /* XXX : make this higher level */
357 debug (19, result ? 2 : 1) ("MemObject::isContiguous: Returning %s\n",
358 result ? "true" : "false");
359 return result;
360 }