]>
Commit | Line | Data |
---|---|---|
528b2c61 | 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 | } |