-
/*
- * DEBUG: section 19 Store Memory Primitives
- * AUTHOR: Robert Collins
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * 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.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+/* DEBUG: section 19 Store Memory Primitives */
+
#include "squid.h"
#include "comm/Connection.h"
#include "Generic.h"
#include "globals.h"
#include "HttpReply.h"
-#include "HttpRequest.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()
}
const char *
-MemObject::storeId() const {
- if (!storeId_.defined()) {
- debugs(20, DBG_IMPORTANT, "Bug: Missing MemObject::storeId value");
+MemObject::storeId() const
+{
+ if (!storeId_.size()) {
+ debugs(20, DBG_IMPORTANT, "ERROR: Squid BUG: Missing MemObject::storeId value");
dump();
storeId_ = "[unknown_URI]";
}
}
const char *
-MemObject::logUri() const {
- return logUri_.defined() ? logUri_.termedBuf() : storeId();
+MemObject::logUri() const
+{
+ return logUri_.size() ? logUri_.termedBuf() : storeId();
}
bool
-MemObject::hasUris() const {
- return storeId_.defined();
+MemObject::hasUris() const
+{
+ return storeId_.size();
}
void
MemObject::setUris(char const *aStoreId, char const *aLogUri, const HttpRequestMethod &aMethod)
{
+ if (hasUris())
+ return;
+
storeId_ = aStoreId;
+ debugs(88, 3, this << " storeId: " << storeId_);
// fast pointer comparison for a common storeCreateEntry(url,url,...) case
- if (!aLogUri || aLogUri == aStoreId)
+ if (!aLogUri || aLogUri == aStoreId)
logUri_.clean(); // use storeId_ by default to minimize copying
else
logUri_ = aLogUri;
#endif
}
-MemObject::MemObject(): smpCollapsed(false)
+MemObject::MemObject()
{
- debugs(20, 3, HERE << "new MemObject " << this);
- _reply = new HttpReply;
- HTTPMSGLOCK(_reply);
-
- object_sz = -1;
-
- /* XXX account log_url */
-
- swapout.decision = SwapOut::swNeedsCheck;
+ debugs(20, 3, "MemObject constructed, this=" << this);
+ ping_reply_callback = nullptr;
+ memset(&start_ping, 0, sizeof(start_ping));
+ reply_ = new HttpReply;
}
MemObject::~MemObject()
{
- debugs(20, 3, HERE << "del MemObject " << this);
- const Ctx ctx = ctx_enter(storeId_.termedBuf()); /* XXX: need URI? */
+ debugs(20, 3, "MemObject destructed, this=" << this);
#if URL_CHECKSUM_DEBUG
checkUrlChecksum();
#endif
- if (!shutting_down) { // Store::Root() is FATALly missing during shutdown
- // TODO: Consider moving these to destroyMemoryObject
- if (xitTable.index >= 0)
- Store::Root().transientsDisconnect(*this);
- if (memCache.index >= 0)
- Store::Root().memoryDisconnect(*this);
-
- 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
-
- HTTPMSGUNLOCK(_reply);
-
- HTTPMSGUNLOCK(request);
-
- ctx_exit(ctx); /* must exit before we free mem->url */
-
- safe_free(vary_headers);
+HttpReply &
+MemObject::adjustableBaseReply()
+{
+ assert(!updatedReply_);
+ return *reply_;
}
void
-MemObject::unlinkRequest()
+MemObject::replaceBaseReply(const HttpReplyPointer &r)
{
- HTTPMSGUNLOCK(request);
+ 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->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_);
}
-HttpReply const *
-MemObject::getReply() const
-{
- return _reply;
-}
-
-void
-MemObject::replaceHttpReply(HttpReply *newrep)
-{
- HTTPMSGUNLOCK(_reply);
- _reply = newrep;
- HTTPMSGLOCK(_reply);
-}
-
struct LowestMemReader : public unary_function<store_client, void> {
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;
void
MemObject::stat(MemBuf * mb) const
{
- mb->Printf("\t%s %s\n",
- RequestMethodStr(method), logUri());
- if (vary_headers)
- mb->Printf("\tvary_headers: %s\n", vary_headers);
- mb->Printf("\tinmem_lo: %" PRId64 "\n", inmem_lo);
- mb->Printf("\tinmem_hi: %" PRId64 "\n", data_hdr.endOffset());
- mb->Printf("\tswapout: %" PRId64 " bytes queued\n",
- swapout.queue_offset);
+ mb->appendf("\t" SQUIDSBUFPH " %s\n", SQUIDSBUFPRINT(method.image()), logUri());
+ if (!vary_headers.isEmpty())
+ mb->appendf("\tvary_headers: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(vary_headers));
+ mb->appendf("\tinmem_lo: %" PRId64 "\n", inmem_lo);
+ mb->appendf("\tinmem_hi: %" PRId64 "\n", data_hdr.endOffset());
+ mb->appendf("\tswapout: %" PRId64 " bytes queued\n", swapout.queue_offset);
if (swapout.sio.getRaw())
- mb->Printf("\tswapout: %" PRId64 " bytes written\n",
- (int64_t) swapout.sio->offset());
+ mb->appendf("\tswapout: %" PRId64 " bytes written\n", (int64_t) swapout.sio->offset());
if (xitTable.index >= 0)
- mb->Printf("\ttransient index: %d state: %d\n",
- xitTable.index, xitTable.io);
+ mb->appendf("\ttransient index: %d state: %d\n", xitTable.index, xitTable.io);
if (memCache.index >= 0)
- mb->Printf("\tmem-cache index: %d state: %d offset: %" PRId64 "\n",
- memCache.index, memCache.io, memCache.offset);
+ mb->appendf("\tmem-cache index: %d state: %d offset: %" PRId64 "\n", memCache.index, memCache.io, memCache.offset);
if (object_sz >= 0)
- mb->Printf("\tobject_sz: %" PRId64 "\n", object_sz);
- if (smpCollapsed)
- mb->Printf("\tsmp-collapsed\n");
+ mb->appendf("\tobject_sz: %" PRId64 "\n", object_sz);
StoreClientStats statsVisitor(mb);
{
const int hdr_sz = endOffset();
assert(hdr_sz >= 0);
- assert(_reply);
- _reply->hdr_sz = hdr_sz;
+ assert(reply_);
+ reply_->hdr_sz = hdr_sz;
}
int64_t
int64_t
MemObject::expectedReplySize() const
{
- debugs(20, 7, HERE << "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;
+
+ // 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;
- if (_reply) {
- const int64_t clen = _reply->bodySize(method);
- debugs(20, 7, HERE << "clen: " << clen);
- if (clen >= 0 && _reply->hdr_sz > 0) // yuck: HttpMsg sets hdr_sz to 0
- return clen + _reply->hdr_sz;
+ 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? */
+ assert(reply_);
+ reply_->reset();
+ updatedReply_ = nullptr;
+ appliedUpdates = false;
}
int64_t
bool
MemObject::readAheadPolicyCanRead() const
{
- const bool canRead = endOffset() - getReply()->hdr_sz <
- lowestMemReaderOffset() + Config.readAheadGap;
+ 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();
new_mem_lo = on_disk - 1;
if (new_mem_lo == -1)
- new_mem_lo = 0; /* the above might become -1 */
+ new_mem_lo = 0; /* the above might become -1 */
data_hdr.freeDataUpto(new_mem_lo);
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)
{
- deferredReads.delayRead(aRead);
+#if USE_DELAY_POOLS
+ if (readAheadPolicyCanRead()) {
+ if (DelayId mostAllowedId = mostBytesAllowed()) {
+ mostAllowedId.delayRead(aRead);
+ return;
+ }
+ }
+#endif
+ 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;
{
return endOffset() - swapout.queue_offset;
}
+