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