]>
Commit | Line | Data |
---|---|---|
1b39caaa | 1 | |
582c2af2 | 2 | #include "squid.h" |
4299f876 | 3 | #include "base/AsyncJobCalls.h" |
3d93a84d | 4 | #include "base/TextException.h" |
1b39caaa | 5 | #include "BodyPipe.h" |
6 | ||
7 | CBDATA_CLASS_INIT(BodyPipe); | |
8 | ||
e7352f30 | 9 | // BodySink is a BodyConsumer class which just consume and drops |
26ac0430 AJ |
10 | // data from a BodyPipe |
11 | class BodySink: public BodyConsumer | |
12 | { | |
e7352f30 | 13 | public: |
5a856d1e AR |
14 | BodySink(const BodyPipe::Pointer &bp): AsyncJob("BodySink"), body_pipe(bp) {} |
15 | virtual ~BodySink() { assert(!body_pipe); } | |
26ac0430 | 16 | |
e7352f30 | 17 | virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer bp) { |
26ac0430 AJ |
18 | size_t contentSize = bp->buf().contentSize(); |
19 | bp->consume(contentSize); | |
e7352f30 | 20 | } |
21 | virtual void noteBodyProductionEnded(BodyPipe::Pointer bp) { | |
5a856d1e | 22 | stopConsumingFrom(body_pipe); |
e7352f30 | 23 | } |
24 | virtual void noteBodyProducerAborted(BodyPipe::Pointer bp) { | |
5a856d1e | 25 | stopConsumingFrom(body_pipe); |
e7352f30 | 26 | } |
5a856d1e AR |
27 | bool doneAll() const {return !body_pipe && AsyncJob::doneAll();} |
28 | ||
29 | private: | |
30 | BodyPipe::Pointer body_pipe; ///< the pipe we are consuming from | |
31 | ||
e7352f30 | 32 | CBDATA_CLASS2(BodySink); |
33 | }; | |
34 | ||
35 | CBDATA_CLASS_INIT(BodySink); | |
36 | ||
37 | // The BodyProducerDialer is an AsyncCall class which used to schedule BodyProducer calls. | |
26ac0430 | 38 | // In addition to a normal AsyncCall checks if the BodyProducer is still the producer of |
e7352f30 | 39 | // the BodyPipe passed as argument |
40 | class BodyProducerDialer: public UnaryMemFunT<BodyProducer, BodyPipe::Pointer> | |
41 | { | |
42 | public: | |
26ac0430 | 43 | typedef UnaryMemFunT<BodyProducer, BodyPipe::Pointer> Parent; |
e7352f30 | 44 | |
4299f876 | 45 | BodyProducerDialer(const BodyProducer::Pointer &aProducer, |
4cb2536f A |
46 | Parent::Method aHandler, BodyPipe::Pointer bp): |
47 | Parent(aProducer, aHandler, bp) {} | |
e7352f30 | 48 | |
26ac0430 | 49 | virtual bool canDial(AsyncCall &call); |
e7352f30 | 50 | }; |
51 | ||
52 | // The BodyConsumerDialer is an AsyncCall class which used to schedule BodyConsumer calls. | |
26ac0430 | 53 | // In addition to a normal AsyncCall checks if the BodyConsumer is still the reciptient |
e7352f30 | 54 | // of the BodyPipe passed as argument |
55 | class BodyConsumerDialer: public UnaryMemFunT<BodyConsumer, BodyPipe::Pointer> | |
56 | { | |
57 | public: | |
26ac0430 | 58 | typedef UnaryMemFunT<BodyConsumer, BodyPipe::Pointer> Parent; |
e7352f30 | 59 | |
4299f876 | 60 | BodyConsumerDialer(const BodyConsumer::Pointer &aConsumer, |
4cb2536f A |
61 | Parent::Method aHandler, BodyPipe::Pointer bp): |
62 | Parent(aConsumer, aHandler, bp) {} | |
e7352f30 | 63 | |
26ac0430 | 64 | virtual bool canDial(AsyncCall &call); |
e7352f30 | 65 | }; |
66 | ||
67 | bool | |
26ac0430 AJ |
68 | BodyProducerDialer::canDial(AsyncCall &call) |
69 | { | |
70 | if (!Parent::canDial(call)) | |
71 | return false; | |
72 | ||
4299f876 | 73 | const BodyProducer::Pointer &producer = job; |
26ac0430 AJ |
74 | BodyPipe::Pointer pipe = arg1; |
75 | if (!pipe->stillProducing(producer)) { | |
76 | debugs(call.debugSection, call.debugLevel, HERE << producer << | |
77 | " no longer producing for " << pipe->status()); | |
78 | return call.cancel("no longer producing"); | |
79 | } | |
e7352f30 | 80 | |
26ac0430 | 81 | return true; |
e7352f30 | 82 | } |
83 | ||
84 | bool | |
26ac0430 AJ |
85 | BodyConsumerDialer::canDial(AsyncCall &call) |
86 | { | |
87 | if (!Parent::canDial(call)) | |
88 | return false; | |
89 | ||
4299f876 | 90 | const BodyConsumer::Pointer &consumer = job; |
26ac0430 AJ |
91 | BodyPipe::Pointer pipe = arg1; |
92 | if (!pipe->stillConsuming(consumer)) { | |
93 | debugs(call.debugSection, call.debugLevel, HERE << consumer << | |
94 | " no longer consuming from " << pipe->status()); | |
95 | return call.cancel("no longer consuming"); | |
96 | } | |
e7352f30 | 97 | |
26ac0430 | 98 | return true; |
e7352f30 | 99 | } |
100 | ||
e7352f30 | 101 | /* BodyProducer */ |
102 | ||
1b39caaa | 103 | // inform the pipe that we are done and clear the Pointer |
104 | void BodyProducer::stopProducingFor(RefCount<BodyPipe> &pipe, bool atEof) | |
105 | { | |
26ac0430 AJ |
106 | debugs(91,7, HERE << this << " will not produce for " << pipe << |
107 | "; atEof: " << atEof); | |
108 | assert(pipe != NULL); // be strict: the caller state may depend on this | |
109 | pipe->clearProducer(atEof); | |
110 | pipe = NULL; | |
1b39caaa | 111 | } |
112 | ||
e7352f30 | 113 | /* BodyConsumer */ |
114 | ||
1b39caaa | 115 | // inform the pipe that we are done and clear the Pointer |
116 | void BodyConsumer::stopConsumingFrom(RefCount<BodyPipe> &pipe) | |
117 | { | |
26ac0430 AJ |
118 | debugs(91,7, HERE << this << " will not consume from " << pipe); |
119 | assert(pipe != NULL); // be strict: the caller state may depend on this | |
120 | pipe->clearConsumer(); | |
121 | pipe = NULL; | |
1b39caaa | 122 | } |
123 | ||
124 | /* BodyPipe */ | |
125 | ||
126 | BodyPipe::BodyPipe(Producer *aProducer): theBodySize(-1), | |
26ac0430 AJ |
127 | theProducer(aProducer), theConsumer(0), |
128 | thePutSize(0), theGetSize(0), | |
abe286b8 | 129 | mustAutoConsume(false), abortedConsumption(false), isCheckedOut(false) |
1b39caaa | 130 | { |
26ac0430 AJ |
131 | // TODO: teach MemBuf to start with zero minSize |
132 | // TODO: limit maxSize by theBodySize, when known? | |
133 | theBuf.init(2*1024, MaxCapacity); | |
134 | debugs(91,7, HERE << "created BodyPipe" << status()); | |
1b39caaa | 135 | } |
136 | ||
137 | BodyPipe::~BodyPipe() | |
138 | { | |
26ac0430 AJ |
139 | debugs(91,7, HERE << "destroying BodyPipe" << status()); |
140 | assert(!theProducer); | |
141 | assert(!theConsumer); | |
142 | theBuf.clean(); | |
1b39caaa | 143 | } |
144 | ||
47f6e231 | 145 | void BodyPipe::setBodySize(uint64_t aBodySize) |
1b39caaa | 146 | { |
26ac0430 | 147 | assert(!bodySizeKnown()); |
26ac0430 | 148 | assert(thePutSize <= aBodySize); |
1b39caaa | 149 | |
26ac0430 AJ |
150 | // If this assert fails, we need to add code to check for eof and inform |
151 | // the consumer about the eof condition via scheduleBodyEndNotification, | |
152 | // because just setting a body size limit may trigger the eof condition. | |
153 | assert(!theConsumer); | |
1b39caaa | 154 | |
26ac0430 AJ |
155 | theBodySize = aBodySize; |
156 | debugs(91,7, HERE << "set body size" << status()); | |
1b39caaa | 157 | } |
158 | ||
47f6e231 | 159 | uint64_t BodyPipe::bodySize() const |
1b39caaa | 160 | { |
26ac0430 AJ |
161 | assert(bodySizeKnown()); |
162 | return static_cast<uint64_t>(theBodySize); | |
1b39caaa | 163 | } |
164 | ||
47f6e231 | 165 | bool BodyPipe::expectMoreAfter(uint64_t offset) const |
1b39caaa | 166 | { |
26ac0430 AJ |
167 | assert(theGetSize <= offset); |
168 | return offset < thePutSize || // buffer has more now or | |
169 | (!productionEnded() && mayNeedMoreData()); // buffer will have more | |
1b39caaa | 170 | } |
171 | ||
172 | bool BodyPipe::exhausted() const | |
173 | { | |
26ac0430 | 174 | return !expectMoreAfter(theGetSize); |
1b39caaa | 175 | } |
176 | ||
610d8f3b | 177 | uint64_t BodyPipe::unproducedSize() const |
1b39caaa | 178 | { |
26ac0430 | 179 | return bodySize() - thePutSize; // bodySize() asserts that size is known |
1b39caaa | 180 | } |
181 | ||
83c51da9 CT |
182 | void BodyPipe::expectProductionEndAfter(uint64_t size) |
183 | { | |
184 | const uint64_t expectedSize = thePutSize + size; | |
185 | if (bodySizeKnown()) | |
186 | Must(bodySize() == expectedSize); | |
187 | else | |
188 | theBodySize = expectedSize; | |
189 | } | |
190 | ||
1b39caaa | 191 | void |
192 | BodyPipe::clearProducer(bool atEof) | |
193 | { | |
4299f876 | 194 | if (theProducer.set()) { |
26ac0430 | 195 | debugs(91,7, HERE << "clearing BodyPipe producer" << status()); |
4299f876 | 196 | theProducer.clear(); |
26ac0430 AJ |
197 | if (atEof) { |
198 | if (!bodySizeKnown()) | |
199 | theBodySize = thePutSize; | |
e1381638 AJ |
200 | else if (bodySize() != thePutSize) |
201 | debugs(91,3, HERE << "aborting on premature eof" << status()); | |
26ac0430 AJ |
202 | } else { |
203 | // asserta that we can detect the abort if the consumer joins later | |
204 | assert(!bodySizeKnown() || bodySize() != thePutSize); | |
205 | } | |
206 | scheduleBodyEndNotification(); | |
207 | } | |
1b39caaa | 208 | } |
209 | ||
210 | size_t | |
350e2aec | 211 | BodyPipe::putMoreData(const char *aBuffer, size_t size) |
1b39caaa | 212 | { |
26ac0430 | 213 | if (bodySizeKnown()) |
d85c3078 | 214 | size = min((uint64_t)size, unproducedSize()); |
26ac0430 AJ |
215 | |
216 | const size_t spaceSize = static_cast<size_t>(theBuf.potentialSpaceSize()); | |
d85c3078 | 217 | if ((size = min(size, spaceSize))) { |
350e2aec | 218 | theBuf.append(aBuffer, size); |
26ac0430 AJ |
219 | postAppend(size); |
220 | return size; | |
221 | } | |
222 | return 0; | |
1b39caaa | 223 | } |
224 | ||
225 | bool | |
4299f876 | 226 | BodyPipe::setConsumerIfNotLate(const Consumer::Pointer &aConsumer) |
1b39caaa | 227 | { |
26ac0430 | 228 | assert(!theConsumer); |
4299f876 | 229 | assert(aConsumer.set()); // but might be invalid |
26ac0430 AJ |
230 | |
231 | // TODO: convert this into an exception and remove IfNotLate suffix | |
232 | // If there is something consumed already, we are in an auto-consuming mode | |
233 | // and it is too late to attach a real consumer to the pipe. | |
234 | if (theGetSize > 0) { | |
235 | assert(mustAutoConsume); | |
236 | return false; | |
237 | } | |
1b39caaa | 238 | |
abe286b8 AR |
239 | Must(!abortedConsumption); // did not promise to never consume |
240 | ||
26ac0430 AJ |
241 | theConsumer = aConsumer; |
242 | debugs(91,7, HERE << "set consumer" << status()); | |
243 | if (theBuf.hasContent()) | |
244 | scheduleBodyDataNotification(); | |
245 | if (!theProducer) | |
246 | scheduleBodyEndNotification(); | |
1b39caaa | 247 | |
26ac0430 | 248 | return true; |
1b39caaa | 249 | } |
250 | ||
251 | void | |
26ac0430 AJ |
252 | BodyPipe::clearConsumer() |
253 | { | |
4299f876 | 254 | if (theConsumer.set()) { |
26ac0430 | 255 | debugs(91,7, HERE << "clearing consumer" << status()); |
4299f876 | 256 | theConsumer.clear(); |
abe286b8 AR |
257 | // do not abort if we have not consumed so that HTTP or ICAP can retry |
258 | // benign xaction failures due to persistent connection race conditions | |
259 | if (consumedSize()) | |
260 | expectNoConsumption(); | |
261 | } | |
262 | } | |
263 | ||
264 | void | |
265 | BodyPipe::expectNoConsumption() | |
266 | { | |
b84d327b AR |
267 | // We may be called multiple times because multiple jobs on the consumption |
268 | // chain may realize that there will be no more setConsumer() calls (e.g., | |
269 | // consuming code and retrying code). It is both difficult and not really | |
270 | // necessary for them to coordinate their expectNoConsumption() calls. | |
271 | ||
272 | // As a consequence, we may be called when we are auto-consuming already. | |
273 | ||
abe286b8 | 274 | if (!abortedConsumption && !exhausted()) { |
b84d327b | 275 | // Before we abort, any regular consumption should be over and auto |
d8eddc48 | 276 | // consumption must not be started. |
b84d327b AR |
277 | Must(!theConsumer); |
278 | ||
e6f9e263 A |
279 | AsyncCall::Pointer call= asyncCall(91, 7, |
280 | "BodyProducer::noteBodyConsumerAborted", | |
281 | BodyProducerDialer(theProducer, | |
282 | &BodyProducer::noteBodyConsumerAborted, this)); | |
283 | ScheduleCallHere(call); | |
284 | abortedConsumption = true; | |
b84d327b AR |
285 | |
286 | // in case somebody enabled auto-consumption before regular one aborted | |
287 | if (mustAutoConsume) | |
288 | startAutoConsumption(); | |
26ac0430 | 289 | } |
1b39caaa | 290 | } |
291 | ||
292 | size_t | |
350e2aec | 293 | BodyPipe::getMoreData(MemBuf &aMemBuffer) |
1b39caaa | 294 | { |
26ac0430 AJ |
295 | if (!theBuf.hasContent()) |
296 | return 0; // did not touch the possibly uninitialized buf | |
1b39caaa | 297 | |
350e2aec FC |
298 | if (aMemBuffer.isNull()) |
299 | aMemBuffer.init(); | |
300 | const size_t size = min(theBuf.contentSize(), aMemBuffer.potentialSpaceSize()); | |
301 | aMemBuffer.append(theBuf.content(), size); | |
26ac0430 AJ |
302 | theBuf.consume(size); |
303 | postConsume(size); | |
304 | return size; // cannot be zero if we called buf.init above | |
1b39caaa | 305 | } |
306 | ||
307 | void | |
308 | BodyPipe::consume(size_t size) | |
309 | { | |
26ac0430 AJ |
310 | theBuf.consume(size); |
311 | postConsume(size); | |
1b39caaa | 312 | } |
313 | ||
26ac0430 | 314 | // In the AutoConsumption mode the consumer has gone but the producer continues |
e7352f30 | 315 | // producing data. We are using a BodySink BodyConsumer which just discards the produced data. |
1b39caaa | 316 | void |
26ac0430 AJ |
317 | BodyPipe::enableAutoConsumption() |
318 | { | |
319 | mustAutoConsume = true; | |
320 | debugs(91,5, HERE << "enabled auto consumption" << status()); | |
321 | if (!theConsumer && theBuf.hasContent()) | |
322 | startAutoConsumption(); | |
5121d48e AR |
323 | } |
324 | ||
325 | // start auto consumption by creating body sink | |
326 | void | |
327 | BodyPipe::startAutoConsumption() | |
328 | { | |
26ac0430 AJ |
329 | Must(mustAutoConsume); |
330 | Must(!theConsumer); | |
5a856d1e | 331 | theConsumer = new BodySink(this); |
26ac0430 AJ |
332 | debugs(91,7, HERE << "starting auto consumption" << status()); |
333 | scheduleBodyDataNotification(); | |
1b39caaa | 334 | } |
335 | ||
336 | MemBuf & | |
26ac0430 AJ |
337 | BodyPipe::checkOut() |
338 | { | |
339 | assert(!isCheckedOut); | |
340 | isCheckedOut = true; | |
341 | return theBuf; | |
1b39caaa | 342 | } |
343 | ||
344 | void | |
345 | BodyPipe::checkIn(Checkout &checkout) | |
346 | { | |
26ac0430 AJ |
347 | assert(isCheckedOut); |
348 | isCheckedOut = false; | |
349 | const size_t currentSize = theBuf.contentSize(); | |
350 | if (checkout.checkedOutSize > currentSize) | |
351 | postConsume(checkout.checkedOutSize - currentSize); | |
e1381638 AJ |
352 | else if (checkout.checkedOutSize < currentSize) |
353 | postAppend(currentSize - checkout.checkedOutSize); | |
1b39caaa | 354 | } |
355 | ||
356 | void | |
357 | BodyPipe::undoCheckOut(Checkout &checkout) | |
358 | { | |
26ac0430 AJ |
359 | assert(isCheckedOut); |
360 | const size_t currentSize = theBuf.contentSize(); | |
361 | // We can only undo if size did not change, and even that carries | |
362 | // some risk. If this becomes a problem, the code checking out | |
363 | // raw buffers should always check them in (possibly unchanged) | |
364 | // instead of relying on the automated undo mechanism of Checkout. | |
365 | // The code can always use a temporary buffer to accomplish that. | |
87728a5b | 366 | Must(checkout.checkedOutSize == currentSize); |
1b39caaa | 367 | } |
368 | ||
369 | // TODO: Optimize: inform consumer/producer about more data/space only if | |
370 | // they used the data/space since we notified them last time. | |
371 | ||
372 | void | |
26ac0430 AJ |
373 | BodyPipe::postConsume(size_t size) |
374 | { | |
375 | assert(!isCheckedOut); | |
376 | theGetSize += size; | |
377 | debugs(91,7, HERE << "consumed " << size << " bytes" << status()); | |
378 | if (mayNeedMoreData()) { | |
379 | AsyncCall::Pointer call= asyncCall(91, 7, | |
380 | "BodyProducer::noteMoreBodySpaceAvailable", | |
381 | BodyProducerDialer(theProducer, | |
382 | &BodyProducer::noteMoreBodySpaceAvailable, this)); | |
383 | ScheduleCallHere(call); | |
384 | } | |
1b39caaa | 385 | } |
386 | ||
387 | void | |
26ac0430 AJ |
388 | BodyPipe::postAppend(size_t size) |
389 | { | |
390 | assert(!isCheckedOut); | |
391 | thePutSize += size; | |
392 | debugs(91,7, HERE << "added " << size << " bytes" << status()); | |
1b39caaa | 393 | |
26ac0430 AJ |
394 | if (mustAutoConsume && !theConsumer && size > 0) |
395 | startAutoConsumption(); | |
5121d48e | 396 | |
26ac0430 AJ |
397 | // We should not consume here even if mustAutoConsume because the |
398 | // caller may not be ready for the data to be consumed during this call. | |
399 | scheduleBodyDataNotification(); | |
1b39caaa | 400 | |
26ac0430 AJ |
401 | if (!mayNeedMoreData()) |
402 | clearProducer(true); // reached end-of-body | |
1b39caaa | 403 | } |
404 | ||
6c56baf6 | 405 | void |
406 | BodyPipe::scheduleBodyDataNotification() | |
407 | { | |
4299f876 | 408 | if (theConsumer.valid()) { // TODO: allow asyncCall() to check this instead |
26ac0430 AJ |
409 | AsyncCall::Pointer call = asyncCall(91, 7, |
410 | "BodyConsumer::noteMoreBodyDataAvailable", | |
411 | BodyConsumerDialer(theConsumer, | |
412 | &BodyConsumer::noteMoreBodyDataAvailable, this)); | |
413 | ScheduleCallHere(call); | |
414 | } | |
6c56baf6 | 415 | } |
416 | ||
1b39caaa | 417 | void |
418 | BodyPipe::scheduleBodyEndNotification() | |
419 | { | |
4299f876 | 420 | if (theConsumer.valid()) { // TODO: allow asyncCall() to check this instead |
26ac0430 AJ |
421 | if (bodySizeKnown() && bodySize() == thePutSize) { |
422 | AsyncCall::Pointer call = asyncCall(91, 7, | |
423 | "BodyConsumer::noteBodyProductionEnded", | |
424 | BodyConsumerDialer(theConsumer, | |
425 | &BodyConsumer::noteBodyProductionEnded, this)); | |
426 | ScheduleCallHere(call); | |
427 | } else { | |
428 | AsyncCall::Pointer call = asyncCall(91, 7, | |
429 | "BodyConsumer::noteBodyProducerAborted", | |
430 | BodyConsumerDialer(theConsumer, | |
431 | &BodyConsumer::noteBodyProducerAborted, this)); | |
432 | ScheduleCallHere(call); | |
433 | } | |
434 | } | |
1b39caaa | 435 | } |
436 | ||
1b39caaa | 437 | // a short temporary string describing buffer status for debugging |
438 | const char *BodyPipe::status() const | |
439 | { | |
350e2aec FC |
440 | static MemBuf outputBuffer; |
441 | outputBuffer.reset(); | |
1b39caaa | 442 | |
350e2aec | 443 | outputBuffer.append(" [", 2); |
1b39caaa | 444 | |
c91ca3ce | 445 | outputBuffer.Printf("%" PRIu64 "<=%" PRIu64, theGetSize, thePutSize); |
1b39caaa | 446 | if (theBodySize >= 0) |
c91ca3ce | 447 | outputBuffer.Printf("<=%" PRId64, theBodySize); |
26ac0430 | 448 | else |
350e2aec | 449 | outputBuffer.append("<=?", 3); |
1b39caaa | 450 | |
350e2aec | 451 | outputBuffer.Printf(" %d+%d", (int)theBuf.contentSize(), (int)theBuf.spaceSize()); |
1b39caaa | 452 | |
350e2aec | 453 | outputBuffer.Printf(" pipe%p", this); |
4299f876 AR |
454 | if (theProducer.set()) |
455 | outputBuffer.Printf(" prod%p", theProducer.get()); | |
456 | if (theConsumer.set()) | |
457 | outputBuffer.Printf(" cons%p", theConsumer.get()); | |
1b39caaa | 458 | |
26ac0430 | 459 | if (mustAutoConsume) |
350e2aec | 460 | outputBuffer.append(" A", 2); |
abe286b8 AR |
461 | if (abortedConsumption) |
462 | outputBuffer.append(" !C", 3); | |
26ac0430 | 463 | if (isCheckedOut) |
350e2aec | 464 | outputBuffer.append(" L", 2); // Locked |
1b39caaa | 465 | |
350e2aec | 466 | outputBuffer.append("]", 1); |
1b39caaa | 467 | |
350e2aec | 468 | outputBuffer.terminate(); |
1b39caaa | 469 | |
350e2aec | 470 | return outputBuffer.content(); |
1b39caaa | 471 | } |
472 | ||
1b39caaa | 473 | /* BodyPipeCheckout */ |
474 | ||
475 | BodyPipeCheckout::BodyPipeCheckout(BodyPipe &aPipe): pipe(aPipe), | |
26ac0430 AJ |
476 | buf(aPipe.checkOut()), offset(aPipe.consumedSize()), |
477 | checkedOutSize(buf.contentSize()), checkedIn(false) | |
1b39caaa | 478 | { |
479 | } | |
480 | ||
481 | BodyPipeCheckout::~BodyPipeCheckout() | |
482 | { | |
87728a5b AR |
483 | if (!checkedIn) { |
484 | // Do not pipe.undoCheckOut(*this) because it asserts or throws | |
485 | // TODO: consider implementing the long-term solution discussed at | |
486 | // http://www.mail-archive.com/squid-dev@squid-cache.org/msg07910.html | |
487 | debugs(91,2, HERE << "Warning: cannot undo BodyPipeCheckout"); | |
488 | pipe.checkIn(*this); | |
489 | } | |
1b39caaa | 490 | } |
491 | ||
492 | void | |
493 | BodyPipeCheckout::checkIn() | |
494 | { | |
26ac0430 AJ |
495 | assert(!checkedIn); |
496 | pipe.checkIn(*this); | |
497 | checkedIn = true; | |
1b39caaa | 498 | } |
499 | ||
1b39caaa | 500 | BodyPipeCheckout::BodyPipeCheckout(const BodyPipeCheckout &c): pipe(c.pipe), |
26ac0430 AJ |
501 | buf(c.buf), offset(c.offset), checkedOutSize(c.checkedOutSize), |
502 | checkedIn(c.checkedIn) | |
1b39caaa | 503 | { |
26ac0430 | 504 | assert(false); // prevent copying |
1b39caaa | 505 | } |
506 | ||
507 | BodyPipeCheckout & | |
508 | BodyPipeCheckout::operator =(const BodyPipeCheckout &) | |
509 | { | |
26ac0430 AJ |
510 | assert(false); // prevent assignment |
511 | return *this; | |
1b39caaa | 512 | } |