/*
- * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "HttpReply.h"
#include "MemBuf.h"
#include "MemObject.h"
-#include "profiler/Profiler.h"
#include "SquidConfig.h"
#include "Store.h"
#include "StoreClient.h"
#endif
-RemovalPolicy * mem_policy = NULL;
+RemovalPolicy * mem_policy = nullptr;
size_t
MemObject::inUseCount()
MemObject::storeId() const
{
if (!storeId_.size()) {
- debugs(20, DBG_IMPORTANT, "Bug: Missing MemObject::storeId value");
+ debugs(20, DBG_IMPORTANT, "ERROR: Squid BUG: Missing MemObject::storeId value");
dump();
storeId_ = "[unknown_URI]";
}
#endif
}
-MemObject::MemObject() :
- inmem_lo(0),
- nclients(0),
- smpCollapsed(false),
- ping_reply_callback(nullptr),
- ircb_data(nullptr),
- id(0),
- object_sz(-1),
- swap_hdr_sz(0),
-#if URL_CHECKSUM_DEBUG
- chksum(0),
-#endif
- vary_headers(nullptr)
+MemObject::MemObject()
{
- debugs(20, 3, "new MemObject " << this);
+ debugs(20, 3, "MemObject constructed, this=" << this);
+ ping_reply_callback = nullptr;
memset(&start_ping, 0, sizeof(start_ping));
- memset(&abort, 0, sizeof(abort));
reply_ = new HttpReply;
}
MemObject::~MemObject()
{
- debugs(20, 3, "del MemObject " << this);
- const Ctx ctx = ctx_enter(hasUris() ? urlXXX() : "[unknown_ctx]");
+ debugs(20, 3, "MemObject destructed, this=" << this);
#if URL_CHECKSUM_DEBUG
checkUrlChecksum();
#endif
- if (!shutting_down) { // Store::Root() is FATALly missing during shutdown
- assert(xitTable.index < 0);
- assert(memCache.index < 0);
- assert(swapout.sio == NULL);
- }
+ assert(xitTable.index < 0);
+ assert(memCache.index < 0);
+ assert(swapout.sio == nullptr);
data_hdr.freeContent();
+}
-#if 0
- /*
- * There is no way to abort FD-less clients, so they might
- * still have mem->clients set.
- */
- assert(clients.head == NULL);
-
-#endif
+HttpReply &
+MemObject::adjustableBaseReply()
+{
+ assert(!updatedReply_);
+ return *reply_;
+}
- ctx_exit(ctx); /* must exit before we free mem->url */
+void
+MemObject::replaceBaseReply(const HttpReplyPointer &r)
+{
+ assert(r);
+ reply_ = r;
+ updatedReply_ = nullptr;
}
void
MemObject::write(const StoreIOBuffer &writeBuffer)
{
- PROF_start(MemObject_write);
debugs(19, 6, "memWrite: offset " << writeBuffer.offset << " len " << writeBuffer.length);
/* We don't separate out mime headers yet, so ensure that the first
assert (data_hdr.endOffset() || writeBuffer.offset == 0);
assert (data_hdr.write (writeBuffer));
- PROF_stop(MemObject_write);
}
void
MemObject::dump() const
{
data_hdr.dump();
-#if 0
- /* do we want this one? */
- debugs(20, DBG_IMPORTANT, "MemObject->data.origin_offset: " << (data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0));
-#endif
- debugs(20, DBG_IMPORTANT, "MemObject->start_ping: " << start_ping.tv_sec << "."<< std::setfill('0') << std::setw(6) << start_ping.tv_usec);
+ debugs(20, DBG_IMPORTANT, "MemObject->start_ping: " << start_ping);
debugs(20, DBG_IMPORTANT, "MemObject->inmem_hi: " << data_hdr.endOffset());
debugs(20, DBG_IMPORTANT, "MemObject->inmem_lo: " << inmem_lo);
debugs(20, DBG_IMPORTANT, "MemObject->nclients: " << nclients);
debugs(20, DBG_IMPORTANT, "MemObject->reply: " << reply_);
+ debugs(20, DBG_IMPORTANT, "MemObject->updatedReply: " << updatedReply_);
+ debugs(20, DBG_IMPORTANT, "MemObject->appliedUpdates: " << appliedUpdates);
debugs(20, DBG_IMPORTANT, "MemObject->request: " << request);
debugs(20, DBG_IMPORTANT, "MemObject->logUri: " << logUri_);
debugs(20, DBG_IMPORTANT, "MemObject->storeId: " << storeId_);
LowestMemReader(int64_t seed):current(seed) {}
void operator() (store_client const &x) {
- if (x.memReaderHasLowerOffset(current))
- current = x.copyInto.offset;
+ if (x.getType() == STORE_MEM_CLIENT)
+ current = std::min(current, x.readOffset());
}
int64_t current;
mb->appendf("\tmem-cache index: %d state: %d offset: %" PRId64 "\n", memCache.index, memCache.io, memCache.offset);
if (object_sz >= 0)
mb->appendf("\tobject_sz: %" PRId64 "\n", object_sz);
- if (smpCollapsed)
- mb->appendf("\tsmp-collapsed\n");
StoreClientStats statsVisitor(mb);
int64_t
MemObject::expectedReplySize() const
{
- debugs(20, 7, "object_sz: " << object_sz);
- if (object_sz >= 0) // complete() has been called; we know the exact answer
+ if (object_sz >= 0) {
+ debugs(20, 7, object_sz << " frozen by complete()");
return object_sz;
+ }
+
+ const auto hdr_sz = baseReply().hdr_sz;
- if (reply_) {
- const int64_t clen = reply_->bodySize(method);
- debugs(20, 7, "clen: " << clen);
- if (clen >= 0 && reply_->hdr_sz > 0) // yuck: Http::Message sets hdr_sz to 0
- return clen + reply_->hdr_sz;
+ // Cannot predict future length using an empty/unset or HTTP/0 reply.
+ // For any HTTP/1 reply, hdr_sz is positive -- status-line cannot be empty.
+ if (hdr_sz <= 0)
+ return -1;
+
+ const auto clen = baseReply().bodySize(method);
+ if (clen < 0) {
+ debugs(20, 7, "unknown; hdr: " << hdr_sz);
+ return -1;
}
- return -1; // not enough information to predict
+ const auto messageSize = clen + hdr_sz;
+ debugs(20, 7, messageSize << " hdr: " << hdr_sz << " clen: " << clen);
+ return messageSize;
}
void
MemObject::reset()
{
- assert(swapout.sio == NULL);
+ assert(swapout.sio == nullptr);
data_hdr.freeContent();
inmem_lo = 0;
/* Should we check for clients? */
- if (reply_)
- reply_->reset();
+ assert(reply_);
+ reply_->reset();
+ updatedReply_ = nullptr;
+ appliedUpdates = false;
}
int64_t
bool
MemObject::readAheadPolicyCanRead() const
{
- const bool canRead = endOffset() - getReply()->hdr_sz <
+ const auto savedHttpHeaders = baseReply().hdr_sz;
+ const bool canRead = endOffset() - savedHttpHeaders <
lowestMemReaderOffset() + Config.readAheadGap;
if (!canRead) {
- debugs(19, 9, "no: " << endOffset() << '-' << getReply()->hdr_sz <<
+ debugs(19, 5, "no: " << endOffset() << '-' << savedHttpHeaders <<
" < " << lowestMemReaderOffset() << '+' << Config.readAheadGap);
}
* yet.
*/
- if (swapout.sio.getRaw() == NULL)
+ if (swapout.sio.getRaw() == nullptr)
return 0;
int64_t nwritten = swapout.sio->offset();
DelayId largestAllowance = mostBytesAllowed ();
return largestAllowance.bytesWanted(0, max);
}
+#else
+ (void)ignoreDelayPools;
#endif
return max;
store_client *sc = (store_client *) node->data;
sc->delayId.setNoDelay(newValue);
}
-
+#else
+ (void)newValue;
#endif
}
void
-MemObject::delayRead(DeferredRead const &aRead)
+MemObject::delayRead(const AsyncCall::Pointer &aRead)
{
#if USE_DELAY_POOLS
if (readAheadPolicyCanRead()) {
}
}
#endif
- deferredReads.delayRead(aRead);
+ deferredReads.delay(aRead);
}
void
MemObject::kickReads()
{
- deferredReads.kickReads(-1);
+ deferredReads.schedule();
}
#if USE_DELAY_POOLS
for (dlink_node *node = clients.head; node; node = node->next) {
store_client *sc = (store_client *) node->data;
-#if 0
- /* This test is invalid because the client may be writing data
- * and thus will want data immediately.
- * If we include the test, there is a race condition when too much
- * data is read - if all sc's are writing when a read is scheduled.
- * XXX: fixme.
- */
-
- if (!sc->callbackPending())
- /* not waiting for more data */
- continue;
-
-#endif
- j = sc->delayId.bytesWanted(0, sc->copyInto.length);
+ j = sc->bytesWanted();
if (j > jmax) {
jmax = j;