stmem.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 /* DEBUG: section 19 Store Memory Primitives */
10 
11 #include "squid.h"
12 #include "Generic.h"
13 #include "HttpReply.h"
14 #include "mem_node.h"
15 #include "MemObject.h"
16 #include "stmem.h"
17 
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  */
25 char *
27 {
28  assert(!aNode->write_pending);
29  aNode->write_pending = true;
30  return aNode->data;
31 }
32 
33 int64_t
35 {
36  const SplayNode<mem_node *> *theStart = nodes.start();
37 
38  if (theStart)
39  return theStart->data->nodeBuffer.offset;
40 
41  return 0;
42 }
43 
44 int64_t
46 {
47  int64_t result = 0;
48  const SplayNode<mem_node *> *theEnd = nodes.finish();
49 
50  if (theEnd)
51  result = theEnd->data->dataRange().end;
52 
53  assert (result == inmem_hi);
54 
55  return result;
56 }
57 
58 void
60 {
61  nodes.destroy();
62  inmem_hi = 0;
63  debugs(19, 9, this << " hi: " << inmem_hi);
64 }
65 
66 bool
68 {
69  if (aNode->write_pending) {
70  debugs(0, DBG_CRITICAL, "ERROR: cannot unlink mem_node " << aNode << " while write_pending");
71  return false;
72  }
73 
74  debugs(19, 8, this << " removing " << aNode);
75  nodes.remove (aNode, NodeCompare);
76  delete aNode;
77  return true;
78 }
79 
80 int64_t
81 mem_hdr::freeDataUpto(int64_t target_offset)
82 {
83  debugs(19, 8, this << " up to " << target_offset);
84  /* keep the last one to avoid change to other part of code */
85  SplayNode<mem_node*> const * theStart;
86 
87  while ((theStart = nodes.start())) {
88  if (theStart == nodes.finish())
89  break;
90 
91  if (theStart->data->end() > target_offset )
92  break;
93 
94  if (!unlink(theStart->data))
95  break;
96  }
97 
98  return lowestOffset ();
99 }
100 
101 size_t
102 mem_hdr::writeAvailable(mem_node *aNode, int64_t location, size_t amount, char const *source)
103 {
104  /* if we attempt to overwrite existing data or leave a gap within a node */
105  assert (location == aNode->nodeBuffer.offset + (int64_t)aNode->nodeBuffer.length);
106  /* And we are not at the end of the node */
107  assert (aNode->canAccept (location));
108 
109  /* these two can go I think */
110  assert (location - aNode->nodeBuffer.offset == (int64_t)aNode->nodeBuffer.length);
111  size_t copyLen = min(amount, aNode->space());
112 
113  memcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen);
114 
115  debugs(19, 9, this << " hi: " << inmem_hi);
116  if (inmem_hi <= location)
117  inmem_hi = location + copyLen;
118 
119  /* Adjust the ptr and len according to what was deposited in the page */
120  aNode->nodeBuffer.length += copyLen;
121 
122  debugs(19, 9, this << " hi: " << inmem_hi);
123  debugs(19, 9, this << " hi: " << endOffset());
124  return copyLen;
125 }
126 
127 void
129 {
130  nodes.insert (aNode, NodeCompare);
131 }
132 
133 /* returns a mem_node that contains location..
134  * If no node contains the start, it returns NULL.
135  */
136 mem_node *
137 mem_hdr::getBlockContainingLocation (int64_t location) const
138 {
139  // Optimize: do not create a whole mem_node just to store location
140  mem_node target (location);
141  target.nodeBuffer.length = 1;
142  mem_node *const *result = nodes.find (&target, NodeCompare);
143 
144  if (result)
145  return *result;
146 
147  return nullptr;
148 }
149 
150 size_t
151 mem_hdr::copyAvailable(mem_node *aNode, int64_t location, size_t amount, char *target) const
152 {
153  if (aNode->nodeBuffer.offset > location)
154  return 0;
155 
156  assert (aNode->nodeBuffer.offset <= location);
157 
158  assert (aNode->end() > location);
159 
160  size_t copyOffset = location - aNode->nodeBuffer.offset;
161 
162  size_t copyLen = min(amount, aNode->nodeBuffer.length - copyOffset);
163 
164  memcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen);
165 
166  return copyLen;
167 }
168 
169 void
171 {
172  debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << ".");
173  std::ostringstream result;
174  PointerPrinter<mem_node *> foo(result, " - ");
175  getNodes().visit(foo);
176  debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << ".");
177 }
178 
179 /* XXX: how do we deal with sparse results -
180  * where we have (say)
181  * 0-500 and 1000-1500, but are asked for
182  * 0-2000
183  * Partial answer:
184  * we supply 0-500 and stop.
185  */
186 ssize_t
187 mem_hdr::copy(StoreIOBuffer const &target) const
188 {
189 
190  assert(target.range().end > target.range().start);
191  debugs(19, 6, "memCopy: " << this << " " << target.range());
192 
193  /* we shouldn't ever ask for absent offsets */
194 
195  if (nodes.size() == 0) {
196  debugs(19, DBG_IMPORTANT, "mem_hdr::copy: No data to read");
197  debugDump();
198  assert (0);
199  return 0;
200  }
201 
202  /* RC: the next assert is nearly useless */
203  assert(target.length > 0);
204 
205  /* Seek our way into store */
207 
208  if (!p) {
209  debugs(19, DBG_IMPORTANT, "ERROR: memCopy: could not find start of " << target.range() <<
210  " in memory.");
211  debugDump();
212  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");
213  return 0;
214  }
215 
216  size_t bytes_to_go = target.length;
217  char *ptr_to_buf = target.data;
218  int64_t location = target.offset;
219 
220  /* Start copying beginning with this block until
221  * we're satiated */
222 
223  while (p && bytes_to_go > 0) {
224  size_t bytes_to_copy = copyAvailable (p,
225  location, bytes_to_go, ptr_to_buf);
226 
227  /* hit a sparse patch */
228 
229  if (bytes_to_copy == 0)
230  return target.length - bytes_to_go;
231 
232  location += bytes_to_copy;
233 
234  ptr_to_buf += bytes_to_copy;
235 
236  bytes_to_go -= bytes_to_copy;
237 
238  p = getBlockContainingLocation(location);
239  }
240 
241  return target.length - bytes_to_go;
242 }
243 
244 bool
246 {
247  int64_t currentStart = range.start;
248 
249  while (mem_node *curr = getBlockContainingLocation(currentStart)) {
250  currentStart = curr->end();
251 
252  if (currentStart >= range.end)
253  return true;
254  }
255 
256  return !range.size(); // empty range is contiguous
257 }
258 
259 bool
261 {
262  assert (candidate.offset >= 0);
263  mem_node target(candidate.offset);
264  target.nodeBuffer.length = candidate.length;
265  return nodes.find (&target, NodeCompare);
266 }
267 
268 mem_node *
269 mem_hdr::nodeToRecieve(int64_t offset)
270 {
271  /* case 1: Nothing in memory */
272 
273  if (!nodes.size()) {
274  appendNode (new mem_node(offset));
275  return nodes.start()->data;
276  }
277 
278  mem_node *candidate = nullptr;
279  /* case 2: location fits within an extant node */
280 
281  if (offset > 0) {
282  mem_node search (offset - 1);
283  search.nodeBuffer.length = 1;
284  mem_node *const *leadup = nodes.find (&search, NodeCompare);
285 
286  if (leadup)
287  candidate = *leadup;
288  }
289 
290  if (candidate && candidate->canAccept(offset))
291  return candidate;
292 
293  /* candidate can't accept, so we need a new node */
294  candidate = new mem_node(offset);
295 
296  appendNode (candidate);
297 
298  /* simpler to write than a indented if */
299  return candidate;
300 }
301 
302 bool
303 mem_hdr::write (StoreIOBuffer const &writeBuffer)
304 {
305  debugs(19, 6, "mem_hdr::write: " << this << " " << writeBuffer.range() << " object end " << endOffset());
306 
307  if (unionNotEmpty(writeBuffer)) {
308  debugs(19, DBG_CRITICAL, "mem_hdr::write: writeBuffer: " << writeBuffer.range());
309  debugDump();
310  fatal_dump("Attempt to overwrite already in-memory data. Preceding 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");
311  return false;
312  }
313 
314  assert (writeBuffer.offset >= 0);
315 
316  mem_node *target;
317  int64_t currentOffset = writeBuffer.offset;
318  char *currentSource = writeBuffer.data;
319  size_t len = writeBuffer.length;
320 
321  while (len && (target = nodeToRecieve(currentOffset))) {
322  size_t wrote = writeAvailable(target, currentOffset, len, currentSource);
323  assert (wrote);
324  len -= wrote;
325  currentOffset += wrote;
326  currentSource += wrote;
327  }
328 
329  return true;
330 }
331 
332 mem_hdr::mem_hdr() : inmem_hi(0)
333 {
334  debugs(19, 9, this << " hi: " << inmem_hi);
335 }
336 
338 {
339  freeContent();
340 }
341 
342 /* splay of mem nodes:
343  * conditions:
344  * a = b if a.intersection(b).size > 0;
345  * a < b if a < b
346  */
347 int
348 mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right)
349 {
350  // possibly Range can help us at some point.
351 
352  if (left->dataRange().intersection(right->dataRange()).size() > 0)
353  return 0;
354 
355  return *left < *right ? -1 : 1;
356 }
357 
358 void
360 {
361  debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start());
362  debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish());
363 }
364 
365 size_t
367 {
368  return nodes.size();
369 }
370 
371 const Splay<mem_node *> &
373 {
374  return nodes;
375 }
376 
int64_t freeDataUpto(int64_t)
Definition: stmem.cc:81
const SplayNode< V > * start() const
Definition: splay.h:349
void remove(Value const &, SPLAYCMP *compare)
Definition: splay.h:336
Splay< mem_node * > nodes
Definition: stmem.h:53
static Splay< mem_node * >::SPLAYCMP NodeCompare
Definition: stmem.h:42
const Value * find(FindValue const &, int(*compare)(FindValue const &a, Value const &b)) const
Definition: splay.h:305
bool unionNotEmpty(StoreIOBuffer const &)
Definition: stmem.cc:260
int64_t inmem_hi
Definition: stmem.h:52
void visit(ValueVisitor &) const
left-to-right visit of all stored Values
#define DBG_CRITICAL
Definition: Stream.h:37
Range< int64_t > range() const
Definition: StoreIOBuffer.h:42
StoreIOBuffer nodeBuffer
Definition: mem_node.h:35
void appendNode(mem_node *aNode)
Definition: stmem.cc:128
mem_node * nodeToRecieve(int64_t offset)
Definition: stmem.cc:269
C end
Definition: Range.h:25
int64_t offset
Definition: StoreIOBuffer.h:58
char * NodeGet(mem_node *aNode)
Definition: stmem.cc:26
char data[SM_PAGE_SIZE]
Definition: mem_node.h:37
size_t size() const
Definition: splay.h:381
size_t size() const
Definition: stmem.cc:366
bool write(StoreIOBuffer const &)
Definition: stmem.cc:303
Range< int64_t > dataRange() const
Definition: mem_node.cc:83
S size() const
Definition: Range.h:61
const Splay< mem_node * > & getNodes() const
Definition: stmem.cc:372
int64_t endOffset() const
Definition: stmem.cc:45
Definition: Range.h:18
size_t writeAvailable(mem_node *aNode, int64_t location, size_t amount, char const *source)
Definition: stmem.cc:102
mem_node * getBlockContainingLocation(int64_t location) const
Definition: stmem.cc:137
int64_t lowestOffset() const
Definition: stmem.cc:34
#define assert(EX)
Definition: assert.h:17
mem_hdr()
Definition: stmem.cc:332
bool canAccept(int64_t const &location) const
Definition: mem_node.cc:105
void dump() const
Definition: stmem.cc:359
size_t space() const
Definition: mem_node.cc:89
~mem_hdr()
Definition: stmem.cc:337
void debugDump() const
Definition: stmem.cc:170
void freeContent()
Definition: stmem.cc:59
void fatal_dump(const char *message)
Definition: fatal.cc:78
void destroy(SPLAYFREE *=DefaultFree)
Definition: splay.h:369
bool unlink(mem_node *aNode)
Definition: stmem.cc:67
size_t copyAvailable(mem_node *aNode, int64_t location, size_t amount, char *target) const
Definition: stmem.cc:151
const SplayNode< V > * finish() const
Definition: splay.h:359
ssize_t copy(StoreIOBuffer const &) const
Definition: stmem.cc:187
Value data
Definition: splay.h:25
bool hasContigousContentRange(Range< int64_t > const &range) const
Definition: stmem.cc:245
const Value * insert(const Value &, SPLAYCMP *)
Definition: splay.h:320
#define DBG_IMPORTANT
Definition: Stream.h:38
int64_t end() const
Definition: mem_node.cc:77
bool write_pending
Definition: mem_node.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
C start
Definition: Range.h:24

 

Introduction

Documentation

Support

Miscellaneous