]> git.ipfire.org Git - thirdparty/squid.git/blame - src/stmem.cc
Prep for 3.3.12 and 3.4.4
[thirdparty/squid.git] / src / stmem.cc
CommitLineData
df92bd2a 1
30a4f2a8 2/*
6f6f0853 3 * DEBUG: section 19 Store Memory Primitives
30a4f2a8 4 * AUTHOR: Harvest Derived
5 *
2b6662ba 6 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 7 * ----------------------------------------------------------
30a4f2a8 8 *
2b6662ba 9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
30a4f2a8 17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
26ac0430 22 *
30a4f2a8 23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
26ac0430 27 *
30a4f2a8 28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
cbdec147 30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 31 *
42a503bd 32 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
019dd986 33 */
ed43818f 34
582c2af2
FC
35#include "squid.h"
36#include "Generic.h"
528b2c61 37#include "mem_node.h"
38#include "MemObject.h"
582c2af2
FC
39#include "profiler/Profiler.h"
40#include "stmem.h"
528b2c61 41
12d74f96 42/*
43 * NodeGet() is called to get the data buffer to pass to storeIOWrite().
44 * By setting the write_pending flag here we are assuming that there
45 * will be no other users of NodeGet(). The storeIOWrite() callback
46 * is memNodeWriteComplete(), which, for whatever reason, lives in
47 * mem_node.cc.
48 */
d06925a4 49char *
50mem_hdr::NodeGet(mem_node * aNode)
51{
12d74f96 52 assert(!aNode->write_pending);
3dd52a0b 53 aNode->write_pending = true;
d06925a4 54 return aNode->data;
55}
56
47f6e231 57int64_t
528b2c61 58mem_hdr::lowestOffset () const
59{
42a503bd 60 const SplayNode<mem_node *> *theStart = nodes.start();
61
62 if (theStart)
63 return theStart->data->nodeBuffer.offset;
62e76326 64
528b2c61 65 return 0;
66}
67
47f6e231 68int64_t
528b2c61 69mem_hdr::endOffset () const
70{
47f6e231 71 int64_t result = 0;
b8bad68c 72 const SplayNode<mem_node *> *theEnd = nodes.finish();
62e76326 73
42a503bd 74 if (theEnd)
75 result = theEnd->data->dataRange().end;
62e76326 76
528b2c61 77 assert (result == inmem_hi);
62e76326 78
528b2c61 79 return result;
80}
090089c4 81
d89d1fb6 82void
528b2c61 83mem_hdr::freeContent()
84{
42a503bd 85 nodes.destroy(SplayNode<mem_node *>::DefaultFree);
528b2c61 86 inmem_hi = 0;
e2851fe7 87 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
528b2c61 88}
89
12d74f96 90bool
42a503bd 91mem_hdr::unlink(mem_node *aNode)
528b2c61 92{
12d74f96 93 if (aNode->write_pending) {
fa84c01d 94 debugs(0, DBG_CRITICAL, "cannot unlink mem_node " << aNode << " while write_pending");
12d74f96 95 return false;
96 }
d06925a4 97
12d74f96 98 nodes.remove (aNode, NodeCompare);
99 delete aNode;
100 return true;
090089c4 101}
102
47f6e231 103int64_t
104mem_hdr::freeDataUpto(int64_t target_offset)
528b2c61 105{
106 /* keep the last one to avoid change to other part of code */
810635e3 107 SplayNode<mem_node*> const * theStart;
42a503bd 108
12d74f96 109 while ((theStart = nodes.start())) {
110 if (theStart == nodes.finish())
111 break;
112
47f6e231 113 if (theStart->data->end() > target_offset )
12d74f96 114 break;
115
116 if (!unlink(theStart->data))
117 break;
42a503bd 118 }
62e76326 119
528b2c61 120 assert (lowestOffset () <= target_offset);
62e76326 121
528b2c61 122 return lowestOffset ();
123}
124
62e76326 125int
528b2c61 126mem_hdr::appendToNode(mem_node *aNode, const char *data, int maxLength)
127{
128 size_t result = writeAvailable (aNode, aNode->nodeBuffer.offset + aNode->nodeBuffer.length ,maxLength, data);
129 return result;
130}
131
132size_t
47f6e231 133mem_hdr::writeAvailable(mem_node *aNode, int64_t location, size_t amount, char const *source)
528b2c61 134{
135 /* if we attempt to overwrite existing data or leave a gap within a node */
ed013b6c 136 assert (location == aNode->nodeBuffer.offset + (int64_t)aNode->nodeBuffer.length);
528b2c61 137 /* And we are not at the end of the node */
138 assert (aNode->canAccept (location));
62e76326 139
528b2c61 140 /* these two can go I think */
ed013b6c 141 assert (location - aNode->nodeBuffer.offset == (int64_t)aNode->nodeBuffer.length);
d85c3078 142 size_t copyLen = min(amount, aNode->space());
528b2c61 143
41d00cd3 144 memcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen);
528b2c61 145
e2851fe7 146 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
47f6e231 147 if (inmem_hi <= location)
62e76326 148 inmem_hi = location + copyLen;
149
528b2c61 150 /* Adjust the ptr and len according to what was deposited in the page */
151 aNode->nodeBuffer.length += copyLen;
62e76326 152
e2851fe7
AR
153 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
154 debugs(19, 9, HERE << this << " hi: " << endOffset());
528b2c61 155 return copyLen;
156}
157
158void
159mem_hdr::appendNode (mem_node *aNode)
160{
42a503bd 161 nodes.insert (aNode, NodeCompare);
090089c4 162}
163
e6e5d90d 164void
528b2c61 165mem_hdr::makeAppendSpace()
166{
42a503bd 167 if (!nodes.size()) {
168 appendNode (new mem_node (0));
62e76326 169 return;
090089c4 170 }
62e76326 171
b8bad68c 172 if (!nodes.finish()->data->space())
62e76326 173 appendNode (new mem_node (endOffset()));
174
b8bad68c 175 assert (nodes.finish()->data->space());
528b2c61 176}
177
178void
179mem_hdr::internalAppend(const char *data, int len)
180{
e2851fe7 181 debugs(19, 6, "memInternalAppend: " << this << " len " << len);
62e76326 182
090089c4 183 while (len > 0) {
62e76326 184 makeAppendSpace();
b8bad68c 185 int copied = appendToNode (nodes.finish()->data, data, len);
62e76326 186 assert (copied);
187
188 len -= copied;
189 data += copied;
090089c4 190 }
090089c4 191}
192
528b2c61 193/* returns a mem_node that contains location..
194 * If no node contains the start, it returns NULL.
195 */
196mem_node *
47f6e231 197mem_hdr::getBlockContainingLocation (int64_t location) const
528b2c61 198{
a5c1e941 199 // Optimize: do not create a whole mem_node just to store location
42a503bd 200 mem_node target (location);
201 target.nodeBuffer.length = 1;
202 mem_node *const *result = nodes.find (&target, NodeCompare);
62e76326 203
42a503bd 204 if (result)
205 return *result;
62e76326 206
42a503bd 207 return NULL;
528b2c61 208}
209
210size_t
47f6e231 211mem_hdr::copyAvailable(mem_node *aNode, int64_t location, size_t amount, char *target) const
528b2c61 212{
47f6e231 213 if (aNode->nodeBuffer.offset > location)
62e76326 214 return 0;
215
47f6e231 216 assert (aNode->nodeBuffer.offset <= location);
62e76326 217
528b2c61 218 assert (aNode->end() > location);
62e76326 219
528b2c61 220 size_t copyOffset = location - aNode->nodeBuffer.offset;
62e76326 221
d85c3078 222 size_t copyLen = min(amount, aNode->nodeBuffer.length - copyOffset);
528b2c61 223
41d00cd3 224 memcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen);
62e76326 225
528b2c61 226 return copyLen;
227}
228
b8bad68c 229void
230mem_hdr::debugDump() const
231{
db7778f1 232 debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << ".");
b8bad68c 233 std::ostringstream result;
234 PointerPrinter<mem_node *> foo(result, " - ");
235 for_each (getNodes().begin(), getNodes().end(), foo);
db7778f1 236 debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << ".");
b8bad68c 237}
238
62e76326 239/* FIXME: how do we deal with sparse results -
528b2c61 240 * where we have (say)
26ac0430 241 * 0-500 and 1000-1500, but are asked for
528b2c61 242 * 0-2000
243 * Partial answer:
244 * we supply 0-500 and stop.
245 */
02be0294 246ssize_t
90703668 247mem_hdr::copy(StoreIOBuffer const &target) const
090089c4 248{
528b2c61 249
47f6e231 250 assert(target.range().end > target.range().start);
e2851fe7 251 debugs(19, 6, "memCopy: " << this << " " << target.range());
62e76326 252
b8bad68c 253 /* we shouldn't ever ask for absent offsets */
254
42a503bd 255 if (nodes.size() == 0) {
e0236918 256 debugs(19, DBG_IMPORTANT, "mem_hdr::copy: No data to read");
b8bad68c 257 debugDump();
42a503bd 258 assert (0);
62e76326 259 return 0;
42a503bd 260 }
62e76326 261
528b2c61 262 /* RC: the next assert is nearly useless */
90703668 263 assert(target.length > 0);
528b2c61 264
090089c4 265 /* Seek our way into store */
47f6e231 266 mem_node *p = getBlockContainingLocation(target.offset);
62e76326 267
528b2c61 268 if (!p) {
e0236918 269 debugs(19, DBG_IMPORTANT, "memCopy: could not find start of " << target.range() <<
e4049756 270 " in memory.");
b8bad68c 271 debugDump();
14e9491b 272 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 preceding 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 273 return 0;
090089c4 274 }
62e76326 275
90703668 276 size_t bytes_to_go = target.length;
277 char *ptr_to_buf = target.data;
47f6e231 278 int64_t location = target.offset;
62e76326 279
090089c4 280 /* Start copying begining with this block until
281 * we're satiated */
528b2c61 282
090089c4 283 while (p && bytes_to_go > 0) {
62e76326 284 size_t bytes_to_copy = copyAvailable (p,
285 location, bytes_to_go, ptr_to_buf);
286
287 /* hit a sparse patch */
288
289 if (bytes_to_copy == 0)
90703668 290 return target.length - bytes_to_go;
62e76326 291
292 location += bytes_to_copy;
293
294 ptr_to_buf += bytes_to_copy;
295
296 bytes_to_go -= bytes_to_copy;
297
42a503bd 298 p = getBlockContainingLocation(location);
090089c4 299 }
62e76326 300
90703668 301 return target.length - bytes_to_go;
090089c4 302}
528b2c61 303
304bool
47f6e231 305mem_hdr::hasContigousContentRange(Range<int64_t> const & range) const
528b2c61 306{
47f6e231 307 int64_t currentStart = range.start;
62e76326 308
528b2c61 309 while (mem_node *curr = getBlockContainingLocation(currentStart)) {
62e76326 310 currentStart = curr->end();
311
4c50505b 312 if (currentStart >= range.end)
62e76326 313 return true;
528b2c61 314 }
62e76326 315
9cc44692 316 return !range.size(); // empty range is contigous
528b2c61 317}
318
319bool
320mem_hdr::unionNotEmpty(StoreIOBuffer const &candidate)
321{
528b2c61 322 assert (candidate.offset >= 0);
42a503bd 323 mem_node target(candidate.offset);
324 target.nodeBuffer.length = candidate.length;
325 return nodes.find (&target, NodeCompare);
528b2c61 326}
327
328mem_node *
47f6e231 329mem_hdr::nodeToRecieve(int64_t offset)
528b2c61 330{
331 /* case 1: Nothing in memory */
62e76326 332
42a503bd 333 if (!nodes.size()) {
62e76326 334 appendNode (new mem_node(offset));
42a503bd 335 return nodes.start()->data;
528b2c61 336 }
337
42a503bd 338 mem_node *candidate = NULL;
528b2c61 339 /* case 2: location fits within an extant node */
62e76326 340
42a503bd 341 if (offset > 0) {
342 mem_node search (offset - 1);
343 search.nodeBuffer.length = 1;
344 mem_node *const *leadup = nodes.find (&search, NodeCompare);
345
346 if (leadup)
347 candidate = *leadup;
528b2c61 348 }
349
42a503bd 350 if (candidate && candidate->canAccept(offset))
62e76326 351 return candidate;
528b2c61 352
353 /* candidate can't accept, so we need a new node */
354 candidate = new mem_node(offset);
62e76326 355
528b2c61 356 appendNode (candidate);
62e76326 357
528b2c61 358 /* simpler to write than a indented if */
359 return candidate;
360}
361
528b2c61 362bool
363mem_hdr::write (StoreIOBuffer const &writeBuffer)
364{
1d5161bd 365 PROF_start(mem_hdr_write);
e2851fe7 366 debugs(19, 6, "mem_hdr::write: " << this << " " << writeBuffer.range() << " object end " << endOffset());
528b2c61 367
368 if (unionNotEmpty(writeBuffer)) {
fa84c01d 369 debugs(19, DBG_CRITICAL, "mem_hdr::write: writeBuffer: " << writeBuffer.range());
db7778f1 370 debugDump();
371 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 372 PROF_stop(mem_hdr_write);
62e76326 373 return false;
528b2c61 374 }
375
376 assert (writeBuffer.offset >= 0);
377
378 mem_node *target;
47f6e231 379 int64_t currentOffset = writeBuffer.offset;
528b2c61 380 char *currentSource = writeBuffer.data;
381 size_t len = writeBuffer.length;
62e76326 382
528b2c61 383 while (len && (target = nodeToRecieve(currentOffset))) {
62e76326 384 size_t wrote = writeAvailable(target, currentOffset, len, currentSource);
385 assert (wrote);
386 len -= wrote;
387 currentOffset += wrote;
388 currentSource += wrote;
528b2c61 389 }
390
1d5161bd 391 PROF_stop(mem_hdr_write);
528b2c61 392 return true;
393}
4c50505b 394
42a503bd 395mem_hdr::mem_hdr() : inmem_hi(0)
e2851fe7
AR
396{
397 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
398}
4c50505b 399
400mem_hdr::~mem_hdr()
401{
402 freeContent();
403}
42a503bd 404
405/* splay of mem nodes:
406 * conditions:
407 * a = b if a.intersection(b).size > 0;
408 * a < b if a < b
409 */
410int
411mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right)
412{
413 // possibly Range can help us at some point.
414
415 if (left->dataRange().intersection(right->dataRange()).size() > 0)
416 return 0;
417
418 return *left < *right ? -1 : 1;
419}
420
421void
422mem_hdr::dump() const
423{
e0236918
FC
424 debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start());
425 debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish());
42a503bd 426}
427
428size_t
429mem_hdr::size() const
430{
431 return nodes.size();
432}
433
434mem_node const *
435mem_hdr::start() const
436{
437 const SplayNode<mem_node *> * result = nodes.start();
438
439 if (result)
440 return result->data;
441
442 return NULL;
443}
b8bad68c 444
445const Splay<mem_node *> &
446mem_hdr::getNodes() const
447{
448 return nodes;
449}