]>
Commit | Line | Data |
---|---|---|
df92bd2a | 1 | |
30a4f2a8 | 2 | /* |
d06925a4 | 3 | * $Id: stmem.cc,v 1.88 2005/04/01 21:11:28 serassio Exp $ |
30a4f2a8 | 4 | * |
6f6f0853 | 5 | * DEBUG: section 19 Store Memory Primitives |
30a4f2a8 | 6 | * AUTHOR: Harvest Derived |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
2b6662ba | 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. | |
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 | * |
42a503bd | 34 | * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org> |
019dd986 | 35 | */ |
ed43818f | 36 | |
44a47c6e | 37 | #include "squid.h" |
528b2c61 | 38 | #include "stmem.h" |
39 | #include "mem_node.h" | |
40 | #include "MemObject.h" | |
b8bad68c | 41 | #include "Generic.h" |
528b2c61 | 42 | |
d06925a4 | 43 | char * |
44 | mem_hdr::NodeGet(mem_node * aNode) | |
45 | { | |
46 | aNode->uses++; | |
47 | return aNode->data; | |
48 | } | |
49 | ||
528b2c61 | 50 | int |
51 | mem_hdr::lowestOffset () const | |
52 | { | |
42a503bd | 53 | const SplayNode<mem_node *> *theStart = nodes.start(); |
54 | ||
55 | if (theStart) | |
56 | return theStart->data->nodeBuffer.offset; | |
62e76326 | 57 | |
528b2c61 | 58 | return 0; |
59 | } | |
60 | ||
61 | off_t | |
62 | mem_hdr::endOffset () const | |
63 | { | |
64 | off_t result = 0; | |
b8bad68c | 65 | const SplayNode<mem_node *> *theEnd = nodes.finish(); |
62e76326 | 66 | |
42a503bd | 67 | if (theEnd) |
68 | result = theEnd->data->dataRange().end; | |
62e76326 | 69 | |
528b2c61 | 70 | assert (result == inmem_hi); |
62e76326 | 71 | |
528b2c61 | 72 | return result; |
73 | } | |
090089c4 | 74 | |
d89d1fb6 | 75 | void |
528b2c61 | 76 | mem_hdr::freeContent() |
77 | { | |
42a503bd | 78 | nodes.destroy(SplayNode<mem_node *>::DefaultFree); |
528b2c61 | 79 | inmem_hi = 0; |
80 | } | |
81 | ||
82 | void | |
42a503bd | 83 | mem_hdr::unlink(mem_node *aNode) |
528b2c61 | 84 | { |
42a503bd | 85 | nodes.remove (aNode, NodeCompare); |
d06925a4 | 86 | |
87 | if (!aNode->uses) | |
88 | delete aNode; | |
89 | else | |
90 | aNode->uses--; | |
91 | ||
090089c4 | 92 | } |
93 | ||
d89d1fb6 | 94 | int |
528b2c61 | 95 | mem_hdr::freeDataUpto(int target_offset) |
96 | { | |
97 | /* keep the last one to avoid change to other part of code */ | |
62e76326 | 98 | |
42a503bd | 99 | SplayNode<mem_node*> const * theStart = nodes.start(); |
100 | ||
b8bad68c | 101 | while (theStart && theStart != nodes.finish() && |
42a503bd | 102 | theStart->data->end() <= (size_t) target_offset ) { |
103 | unlink(theStart->data); | |
104 | theStart = nodes.start(); | |
105 | } | |
62e76326 | 106 | |
528b2c61 | 107 | assert (lowestOffset () <= target_offset); |
62e76326 | 108 | |
528b2c61 | 109 | return lowestOffset (); |
110 | } | |
111 | ||
62e76326 | 112 | int |
528b2c61 | 113 | mem_hdr::appendToNode(mem_node *aNode, const char *data, int maxLength) |
114 | { | |
115 | size_t result = writeAvailable (aNode, aNode->nodeBuffer.offset + aNode->nodeBuffer.length ,maxLength, data); | |
116 | return result; | |
117 | } | |
118 | ||
119 | size_t | |
120 | mem_hdr::writeAvailable(mem_node *aNode, size_t location, size_t amount, char const *source) | |
121 | { | |
122 | /* if we attempt to overwrite existing data or leave a gap within a node */ | |
123 | assert (location == aNode->nodeBuffer.offset + aNode->nodeBuffer.length); | |
124 | /* And we are not at the end of the node */ | |
125 | assert (aNode->canAccept (location)); | |
62e76326 | 126 | |
528b2c61 | 127 | /* these two can go I think */ |
e4a67a80 | 128 | assert (location - aNode->nodeBuffer.offset == aNode->nodeBuffer.length); |
528b2c61 | 129 | size_t copyLen = XMIN (amount, aNode->space()); |
130 | ||
131 | xmemcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen); | |
132 | ||
133 | if (inmem_hi <= (off_t) location) | |
62e76326 | 134 | inmem_hi = location + copyLen; |
135 | ||
528b2c61 | 136 | /* Adjust the ptr and len according to what was deposited in the page */ |
137 | aNode->nodeBuffer.length += copyLen; | |
62e76326 | 138 | |
528b2c61 | 139 | mem_node::store_mem_size += copyLen; |
62e76326 | 140 | |
528b2c61 | 141 | return copyLen; |
142 | } | |
143 | ||
144 | void | |
145 | mem_hdr::appendNode (mem_node *aNode) | |
146 | { | |
42a503bd | 147 | nodes.insert (aNode, NodeCompare); |
090089c4 | 148 | } |
149 | ||
e6e5d90d | 150 | void |
528b2c61 | 151 | mem_hdr::makeAppendSpace() |
152 | { | |
42a503bd | 153 | if (!nodes.size()) { |
154 | appendNode (new mem_node (0)); | |
62e76326 | 155 | return; |
090089c4 | 156 | } |
62e76326 | 157 | |
b8bad68c | 158 | if (!nodes.finish()->data->space()) |
62e76326 | 159 | appendNode (new mem_node (endOffset())); |
160 | ||
b8bad68c | 161 | assert (nodes.finish()->data->space()); |
528b2c61 | 162 | } |
163 | ||
164 | void | |
165 | mem_hdr::internalAppend(const char *data, int len) | |
166 | { | |
90703668 | 167 | debugs(19, 6, "memInternalAppend: len " << len); |
62e76326 | 168 | |
090089c4 | 169 | while (len > 0) { |
62e76326 | 170 | makeAppendSpace(); |
b8bad68c | 171 | int copied = appendToNode (nodes.finish()->data, data, len); |
62e76326 | 172 | assert (copied); |
173 | ||
174 | len -= copied; | |
175 | data += copied; | |
090089c4 | 176 | } |
090089c4 | 177 | } |
178 | ||
528b2c61 | 179 | /* returns a mem_node that contains location.. |
180 | * If no node contains the start, it returns NULL. | |
181 | */ | |
182 | mem_node * | |
183 | mem_hdr::getBlockContainingLocation (size_t location) const | |
184 | { | |
42a503bd | 185 | mem_node target (location); |
186 | target.nodeBuffer.length = 1; | |
187 | mem_node *const *result = nodes.find (&target, NodeCompare); | |
62e76326 | 188 | |
42a503bd | 189 | if (result) |
190 | return *result; | |
62e76326 | 191 | |
42a503bd | 192 | return NULL; |
528b2c61 | 193 | } |
194 | ||
195 | size_t | |
196 | mem_hdr::copyAvailable(mem_node *aNode, size_t location, size_t amount, char *target) const | |
197 | { | |
198 | if (aNode->nodeBuffer.offset > (off_t) location) | |
62e76326 | 199 | return 0; |
200 | ||
528b2c61 | 201 | assert (aNode->nodeBuffer.offset <= (off_t) location); |
62e76326 | 202 | |
528b2c61 | 203 | assert (aNode->end() > location); |
62e76326 | 204 | |
528b2c61 | 205 | size_t copyOffset = location - aNode->nodeBuffer.offset; |
62e76326 | 206 | |
528b2c61 | 207 | size_t copyLen = XMIN (amount, aNode->nodeBuffer.length - copyOffset); |
208 | ||
209 | xmemcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen); | |
62e76326 | 210 | |
528b2c61 | 211 | return copyLen; |
212 | } | |
213 | ||
b8bad68c | 214 | void |
215 | mem_hdr::debugDump() const | |
216 | { | |
db7778f1 | 217 | debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << "."); |
b8bad68c | 218 | std::ostringstream result; |
219 | PointerPrinter<mem_node *> foo(result, " - "); | |
220 | for_each (getNodes().begin(), getNodes().end(), foo); | |
db7778f1 | 221 | debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << "."); |
b8bad68c | 222 | } |
223 | ||
62e76326 | 224 | /* FIXME: how do we deal with sparse results - |
528b2c61 | 225 | * where we have (say) |
226 | * 0-500 and 1000-1500, but are asked for | |
227 | * 0-2000 | |
228 | * Partial answer: | |
229 | * we supply 0-500 and stop. | |
230 | */ | |
02be0294 | 231 | ssize_t |
90703668 | 232 | mem_hdr::copy(StoreIOBuffer const &target) const |
090089c4 | 233 | { |
528b2c61 | 234 | |
90703668 | 235 | debugs(19, 6, "memCopy: " << target.range()); |
62e76326 | 236 | |
b8bad68c | 237 | /* we shouldn't ever ask for absent offsets */ |
238 | ||
42a503bd | 239 | if (nodes.size() == 0) { |
b8bad68c | 240 | debugs(19, 1, "mem_hdr::copy: No data to read"); |
241 | debugDump(); | |
42a503bd | 242 | assert (0); |
62e76326 | 243 | return 0; |
42a503bd | 244 | } |
62e76326 | 245 | |
528b2c61 | 246 | /* RC: the next assert is nearly useless */ |
90703668 | 247 | assert(target.length > 0); |
528b2c61 | 248 | |
090089c4 | 249 | /* Seek our way into store */ |
90703668 | 250 | mem_node *p = getBlockContainingLocation((size_t)target.offset); |
62e76326 | 251 | |
528b2c61 | 252 | if (!p) { |
90703668 | 253 | debugs(19, 1, "memCopy: could not find start of " << target.range() << |
e4049756 | 254 | " in memory."); |
b8bad68c | 255 | debugDump(); |
17d97ab3 | 256 | fatal("Squid has attempted to read data from memory that is not present. This is an indication of of (pre-3.0) code that hasn't been updated to deal with sparse objects in memory. Squid should coredump.allowing to review the cause. Immediately preceeding this message is a dump of the available data in the format [start,end). The [ means from the value, the ) means up to the value. I.e. [1,5) means that there are 4 bytes of data, at offsets 1,2,3,4.\n"); |
62e76326 | 257 | return 0; |
090089c4 | 258 | } |
62e76326 | 259 | |
90703668 | 260 | size_t bytes_to_go = target.length; |
261 | char *ptr_to_buf = target.data; | |
262 | off_t location = target.offset; | |
62e76326 | 263 | |
090089c4 | 264 | /* Start copying begining with this block until |
265 | * we're satiated */ | |
528b2c61 | 266 | |
090089c4 | 267 | while (p && bytes_to_go > 0) { |
62e76326 | 268 | size_t bytes_to_copy = copyAvailable (p, |
269 | location, bytes_to_go, ptr_to_buf); | |
270 | ||
271 | /* hit a sparse patch */ | |
272 | ||
273 | if (bytes_to_copy == 0) | |
90703668 | 274 | return target.length - bytes_to_go; |
62e76326 | 275 | |
276 | location += bytes_to_copy; | |
277 | ||
278 | ptr_to_buf += bytes_to_copy; | |
279 | ||
280 | bytes_to_go -= bytes_to_copy; | |
281 | ||
42a503bd | 282 | p = getBlockContainingLocation(location); |
090089c4 | 283 | } |
62e76326 | 284 | |
90703668 | 285 | return target.length - bytes_to_go; |
090089c4 | 286 | } |
528b2c61 | 287 | |
288 | bool | |
4c50505b | 289 | mem_hdr::hasContigousContentRange(Range<size_t> const & range) const |
528b2c61 | 290 | { |
4c50505b | 291 | size_t currentStart = range.start; |
62e76326 | 292 | |
528b2c61 | 293 | while (mem_node *curr = getBlockContainingLocation(currentStart)) { |
62e76326 | 294 | currentStart = curr->end(); |
295 | ||
4c50505b | 296 | if (currentStart >= range.end) |
62e76326 | 297 | return true; |
528b2c61 | 298 | } |
62e76326 | 299 | |
528b2c61 | 300 | return false; |
301 | } | |
302 | ||
303 | bool | |
304 | mem_hdr::unionNotEmpty(StoreIOBuffer const &candidate) | |
305 | { | |
528b2c61 | 306 | assert (candidate.offset >= 0); |
42a503bd | 307 | mem_node target(candidate.offset); |
308 | target.nodeBuffer.length = candidate.length; | |
309 | return nodes.find (&target, NodeCompare); | |
528b2c61 | 310 | } |
311 | ||
312 | mem_node * | |
313 | mem_hdr::nodeToRecieve(off_t offset) | |
314 | { | |
315 | /* case 1: Nothing in memory */ | |
62e76326 | 316 | |
42a503bd | 317 | if (!nodes.size()) { |
62e76326 | 318 | appendNode (new mem_node(offset)); |
42a503bd | 319 | return nodes.start()->data; |
528b2c61 | 320 | } |
321 | ||
42a503bd | 322 | mem_node *candidate = NULL; |
528b2c61 | 323 | /* case 2: location fits within an extant node */ |
62e76326 | 324 | |
42a503bd | 325 | if (offset > 0) { |
326 | mem_node search (offset - 1); | |
327 | search.nodeBuffer.length = 1; | |
328 | mem_node *const *leadup = nodes.find (&search, NodeCompare); | |
329 | ||
330 | if (leadup) | |
331 | candidate = *leadup; | |
528b2c61 | 332 | } |
333 | ||
42a503bd | 334 | if (candidate && candidate->canAccept(offset)) |
62e76326 | 335 | return candidate; |
528b2c61 | 336 | |
337 | /* candidate can't accept, so we need a new node */ | |
338 | candidate = new mem_node(offset); | |
62e76326 | 339 | |
528b2c61 | 340 | appendNode (candidate); |
62e76326 | 341 | |
528b2c61 | 342 | /* simpler to write than a indented if */ |
343 | return candidate; | |
344 | } | |
345 | ||
346 | ||
347 | bool | |
348 | mem_hdr::write (StoreIOBuffer const &writeBuffer) | |
349 | { | |
1d5161bd | 350 | PROF_start(mem_hdr_write); |
90703668 | 351 | debugs(19, 6, "mem_hdr::write: " << writeBuffer.range() << " object end " << endOffset()); |
528b2c61 | 352 | |
353 | if (unionNotEmpty(writeBuffer)) { | |
97545f5e | 354 | debugs(19,0,"mem_hdr::write: writeBuffer: " << writeBuffer.range()); |
db7778f1 | 355 | debugDump(); |
356 | fatal("Attempt to overwrite already in-memory data. Preceeding this there should be a mem_hdr::write output that lists the attempted write, and the currently present data. Please get a 'backtrace full' from this error - using the generated core, and file a bug report with the squid developers including the last 10 lines of cache.log and the backtrace.\n"); | |
1d5161bd | 357 | PROF_stop(mem_hdr_write); |
62e76326 | 358 | return false; |
528b2c61 | 359 | } |
360 | ||
361 | assert (writeBuffer.offset >= 0); | |
362 | ||
363 | mem_node *target; | |
364 | off_t currentOffset = writeBuffer.offset; | |
365 | char *currentSource = writeBuffer.data; | |
366 | size_t len = writeBuffer.length; | |
62e76326 | 367 | |
528b2c61 | 368 | while (len && (target = nodeToRecieve(currentOffset))) { |
62e76326 | 369 | size_t wrote = writeAvailable(target, currentOffset, len, currentSource); |
370 | assert (wrote); | |
371 | len -= wrote; | |
372 | currentOffset += wrote; | |
373 | currentSource += wrote; | |
528b2c61 | 374 | } |
375 | ||
1d5161bd | 376 | PROF_stop(mem_hdr_write); |
528b2c61 | 377 | return true; |
378 | } | |
4c50505b | 379 | |
42a503bd | 380 | mem_hdr::mem_hdr() : inmem_hi(0) |
4c50505b | 381 | {} |
382 | ||
383 | mem_hdr::~mem_hdr() | |
384 | { | |
385 | freeContent(); | |
386 | } | |
42a503bd | 387 | |
388 | /* splay of mem nodes: | |
389 | * conditions: | |
390 | * a = b if a.intersection(b).size > 0; | |
391 | * a < b if a < b | |
392 | */ | |
393 | int | |
394 | mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right) | |
395 | { | |
396 | // possibly Range can help us at some point. | |
397 | ||
398 | if (left->dataRange().intersection(right->dataRange()).size() > 0) | |
399 | return 0; | |
400 | ||
401 | return *left < *right ? -1 : 1; | |
402 | } | |
403 | ||
404 | void | |
405 | mem_hdr::dump() const | |
406 | { | |
90703668 | 407 | debugs(20, 1, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start()); |
408 | debugs(20, 1, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish()); | |
42a503bd | 409 | } |
410 | ||
411 | size_t | |
412 | mem_hdr::size() const | |
413 | { | |
414 | return nodes.size(); | |
415 | } | |
416 | ||
417 | mem_node const * | |
418 | mem_hdr::start() const | |
419 | { | |
420 | const SplayNode<mem_node *> * result = nodes.start(); | |
421 | ||
422 | if (result) | |
423 | return result->data; | |
424 | ||
425 | return NULL; | |
426 | } | |
b8bad68c | 427 | |
428 | const Splay<mem_node *> & | |
429 | mem_hdr::getNodes() const | |
430 | { | |
431 | return nodes; | |
432 | } |