]> git.ipfire.org Git - thirdparty/squid.git/blame - src/stmem.cc
Bug #2016 fix: Prevent BodyPipe async calls from getting seemingly
[thirdparty/squid.git] / src / stmem.cc
CommitLineData
df92bd2a 1
30a4f2a8 2/*
bf8fe701 3 * $Id: stmem.cc,v 1.90 2007/04/28 22:26:37 hno 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
12d74f96 43/*
44 * NodeGet() is called to get the data buffer to pass to storeIOWrite().
45 * By setting the write_pending flag here we are assuming that there
46 * will be no other users of NodeGet(). The storeIOWrite() callback
47 * is memNodeWriteComplete(), which, for whatever reason, lives in
48 * mem_node.cc.
49 */
d06925a4 50char *
51mem_hdr::NodeGet(mem_node * aNode)
52{
12d74f96 53 assert(!aNode->write_pending);
54 aNode->write_pending = 1;
d06925a4 55 return aNode->data;
56}
57
528b2c61 58int
59mem_hdr::lowestOffset () const
60{
42a503bd 61 const SplayNode<mem_node *> *theStart = nodes.start();
62
63 if (theStart)
64 return theStart->data->nodeBuffer.offset;
62e76326 65
528b2c61 66 return 0;
67}
68
69off_t
70mem_hdr::endOffset () const
71{
72 off_t result = 0;
b8bad68c 73 const SplayNode<mem_node *> *theEnd = nodes.finish();
62e76326 74
42a503bd 75 if (theEnd)
76 result = theEnd->data->dataRange().end;
62e76326 77
528b2c61 78 assert (result == inmem_hi);
62e76326 79
528b2c61 80 return result;
81}
090089c4 82
d89d1fb6 83void
528b2c61 84mem_hdr::freeContent()
85{
42a503bd 86 nodes.destroy(SplayNode<mem_node *>::DefaultFree);
528b2c61 87 inmem_hi = 0;
88}
89
12d74f96 90bool
42a503bd 91mem_hdr::unlink(mem_node *aNode)
528b2c61 92{
12d74f96 93 if (aNode->write_pending) {
bf8fe701 94 debugs(0, 0, "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
d89d1fb6 103int
528b2c61 104mem_hdr::freeDataUpto(int target_offset)
105{
106 /* keep the last one to avoid change to other part of code */
62e76326 107
42a503bd 108 SplayNode<mem_node*> const * theStart = nodes.start();
109
12d74f96 110 while ((theStart = nodes.start())) {
111 if (theStart == nodes.finish())
112 break;
113
114 if (theStart->data->end() > (size_t) target_offset )
115 break;
116
117 if (!unlink(theStart->data))
118 break;
42a503bd 119 }
62e76326 120
528b2c61 121 assert (lowestOffset () <= target_offset);
62e76326 122
528b2c61 123 return lowestOffset ();
124}
125
62e76326 126int
528b2c61 127mem_hdr::appendToNode(mem_node *aNode, const char *data, int maxLength)
128{
129 size_t result = writeAvailable (aNode, aNode->nodeBuffer.offset + aNode->nodeBuffer.length ,maxLength, data);
130 return result;
131}
132
133size_t
134mem_hdr::writeAvailable(mem_node *aNode, size_t location, size_t amount, char const *source)
135{
136 /* if we attempt to overwrite existing data or leave a gap within a node */
137 assert (location == aNode->nodeBuffer.offset + aNode->nodeBuffer.length);
138 /* And we are not at the end of the node */
139 assert (aNode->canAccept (location));
62e76326 140
528b2c61 141 /* these two can go I think */
e4a67a80 142 assert (location - aNode->nodeBuffer.offset == aNode->nodeBuffer.length);
528b2c61 143 size_t copyLen = XMIN (amount, aNode->space());
144
145 xmemcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen);
146
147 if (inmem_hi <= (off_t) 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
528b2c61 153 mem_node::store_mem_size += copyLen;
62e76326 154
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{
90703668 181 debugs(19, 6, "memInternalAppend: 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 *
197mem_hdr::getBlockContainingLocation (size_t location) const
198{
42a503bd 199 mem_node target (location);
200 target.nodeBuffer.length = 1;
201 mem_node *const *result = nodes.find (&target, NodeCompare);
62e76326 202
42a503bd 203 if (result)
204 return *result;
62e76326 205
42a503bd 206 return NULL;
528b2c61 207}
208
209size_t
210mem_hdr::copyAvailable(mem_node *aNode, size_t location, size_t amount, char *target) const
211{
212 if (aNode->nodeBuffer.offset > (off_t) location)
62e76326 213 return 0;
214
528b2c61 215 assert (aNode->nodeBuffer.offset <= (off_t) location);
62e76326 216
528b2c61 217 assert (aNode->end() > location);
62e76326 218
528b2c61 219 size_t copyOffset = location - aNode->nodeBuffer.offset;
62e76326 220
528b2c61 221 size_t copyLen = XMIN (amount, aNode->nodeBuffer.length - copyOffset);
222
223 xmemcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen);
62e76326 224
528b2c61 225 return copyLen;
226}
227
b8bad68c 228void
229mem_hdr::debugDump() const
230{
db7778f1 231 debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << ".");
b8bad68c 232 std::ostringstream result;
233 PointerPrinter<mem_node *> foo(result, " - ");
234 for_each (getNodes().begin(), getNodes().end(), foo);
db7778f1 235 debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << ".");
b8bad68c 236}
237
62e76326 238/* FIXME: how do we deal with sparse results -
528b2c61 239 * where we have (say)
240 * 0-500 and 1000-1500, but are asked for
241 * 0-2000
242 * Partial answer:
243 * we supply 0-500 and stop.
244 */
02be0294 245ssize_t
90703668 246mem_hdr::copy(StoreIOBuffer const &target) const
090089c4 247{
528b2c61 248
90703668 249 debugs(19, 6, "memCopy: " << target.range());
62e76326 250
b8bad68c 251 /* we shouldn't ever ask for absent offsets */
252
42a503bd 253 if (nodes.size() == 0) {
b8bad68c 254 debugs(19, 1, "mem_hdr::copy: No data to read");
255 debugDump();
42a503bd 256 assert (0);
62e76326 257 return 0;
42a503bd 258 }
62e76326 259
528b2c61 260 /* RC: the next assert is nearly useless */
90703668 261 assert(target.length > 0);
528b2c61 262
090089c4 263 /* Seek our way into store */
90703668 264 mem_node *p = getBlockContainingLocation((size_t)target.offset);
62e76326 265
528b2c61 266 if (!p) {
90703668 267 debugs(19, 1, "memCopy: could not find start of " << target.range() <<
e4049756 268 " in memory.");
b8bad68c 269 debugDump();
17d97ab3 270 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 271 return 0;
090089c4 272 }
62e76326 273
90703668 274 size_t bytes_to_go = target.length;
275 char *ptr_to_buf = target.data;
276 off_t location = target.offset;
62e76326 277
090089c4 278 /* Start copying begining with this block until
279 * we're satiated */
528b2c61 280
090089c4 281 while (p && bytes_to_go > 0) {
62e76326 282 size_t bytes_to_copy = copyAvailable (p,
283 location, bytes_to_go, ptr_to_buf);
284
285 /* hit a sparse patch */
286
287 if (bytes_to_copy == 0)
90703668 288 return target.length - bytes_to_go;
62e76326 289
290 location += bytes_to_copy;
291
292 ptr_to_buf += bytes_to_copy;
293
294 bytes_to_go -= bytes_to_copy;
295
42a503bd 296 p = getBlockContainingLocation(location);
090089c4 297 }
62e76326 298
90703668 299 return target.length - bytes_to_go;
090089c4 300}
528b2c61 301
302bool
4c50505b 303mem_hdr::hasContigousContentRange(Range<size_t> const & range) const
528b2c61 304{
4c50505b 305 size_t currentStart = range.start;
62e76326 306
528b2c61 307 while (mem_node *curr = getBlockContainingLocation(currentStart)) {
62e76326 308 currentStart = curr->end();
309
4c50505b 310 if (currentStart >= range.end)
62e76326 311 return true;
528b2c61 312 }
62e76326 313
528b2c61 314 return false;
315}
316
317bool
318mem_hdr::unionNotEmpty(StoreIOBuffer const &candidate)
319{
528b2c61 320 assert (candidate.offset >= 0);
42a503bd 321 mem_node target(candidate.offset);
322 target.nodeBuffer.length = candidate.length;
323 return nodes.find (&target, NodeCompare);
528b2c61 324}
325
326mem_node *
327mem_hdr::nodeToRecieve(off_t offset)
328{
329 /* case 1: Nothing in memory */
62e76326 330
42a503bd 331 if (!nodes.size()) {
62e76326 332 appendNode (new mem_node(offset));
42a503bd 333 return nodes.start()->data;
528b2c61 334 }
335
42a503bd 336 mem_node *candidate = NULL;
528b2c61 337 /* case 2: location fits within an extant node */
62e76326 338
42a503bd 339 if (offset > 0) {
340 mem_node search (offset - 1);
341 search.nodeBuffer.length = 1;
342 mem_node *const *leadup = nodes.find (&search, NodeCompare);
343
344 if (leadup)
345 candidate = *leadup;
528b2c61 346 }
347
42a503bd 348 if (candidate && candidate->canAccept(offset))
62e76326 349 return candidate;
528b2c61 350
351 /* candidate can't accept, so we need a new node */
352 candidate = new mem_node(offset);
62e76326 353
528b2c61 354 appendNode (candidate);
62e76326 355
528b2c61 356 /* simpler to write than a indented if */
357 return candidate;
358}
359
360
361bool
362mem_hdr::write (StoreIOBuffer const &writeBuffer)
363{
1d5161bd 364 PROF_start(mem_hdr_write);
90703668 365 debugs(19, 6, "mem_hdr::write: " << writeBuffer.range() << " object end " << endOffset());
528b2c61 366
367 if (unionNotEmpty(writeBuffer)) {
97545f5e 368 debugs(19,0,"mem_hdr::write: writeBuffer: " << writeBuffer.range());
db7778f1 369 debugDump();
370 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 371 PROF_stop(mem_hdr_write);
62e76326 372 return false;
528b2c61 373 }
374
375 assert (writeBuffer.offset >= 0);
376
377 mem_node *target;
378 off_t currentOffset = writeBuffer.offset;
379 char *currentSource = writeBuffer.data;
380 size_t len = writeBuffer.length;
62e76326 381
528b2c61 382 while (len && (target = nodeToRecieve(currentOffset))) {
62e76326 383 size_t wrote = writeAvailable(target, currentOffset, len, currentSource);
384 assert (wrote);
385 len -= wrote;
386 currentOffset += wrote;
387 currentSource += wrote;
528b2c61 388 }
389
1d5161bd 390 PROF_stop(mem_hdr_write);
528b2c61 391 return true;
392}
4c50505b 393
42a503bd 394mem_hdr::mem_hdr() : inmem_hi(0)
4c50505b 395{}
396
397mem_hdr::~mem_hdr()
398{
399 freeContent();
400}
42a503bd 401
402/* splay of mem nodes:
403 * conditions:
404 * a = b if a.intersection(b).size > 0;
405 * a < b if a < b
406 */
407int
408mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right)
409{
410 // possibly Range can help us at some point.
411
412 if (left->dataRange().intersection(right->dataRange()).size() > 0)
413 return 0;
414
415 return *left < *right ? -1 : 1;
416}
417
418void
419mem_hdr::dump() const
420{
90703668 421 debugs(20, 1, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start());
422 debugs(20, 1, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish());
42a503bd 423}
424
425size_t
426mem_hdr::size() const
427{
428 return nodes.size();
429}
430
431mem_node const *
432mem_hdr::start() const
433{
434 const SplayNode<mem_node *> * result = nodes.start();
435
436 if (result)
437 return result->data;
438
439 return NULL;
440}
b8bad68c 441
442const Splay<mem_node *> &
443mem_hdr::getNodes() const
444{
445 return nodes;
446}