]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/store.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / store.cc
index 62371cbdd39cedb16347da4a07b2a45d3b8775e1..89629dec1015daa7c8a75cfbdb0b6fe641547573 100644 (file)
@@ -1,40 +1,18 @@
-
 /*
- * DEBUG: section 20    Storage Manager
- * AUTHOR: Harvest Derived
- *
- * 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-2015 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 20    Storage Manager */
+
 #include "squid.h"
 #include "CacheDigest.h"
 #include "CacheManager.h"
 #include "comm/Connection.h"
+#include "comm/Read.h"
 #include "ETag.h"
 #include "event.h"
 #include "fde.h"
@@ -51,7 +29,6 @@
 #include "RequestFlags.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
-#include "Stack.h"
 #include "StatCounters.h"
 #include "stmem.h"
 #include "Store.h"
 #include "DelayPools.h"
 #endif
 
+/** StoreEntry uses explicit new/delete operators, which set pool chunk size to 2MB
+ * XXX: convert to MEMPROXY_CLASS() API
+ */
+#include "mem/Pool.h"
+
 #include <climits>
+#include <stack>
 
 #define REBUILD_TIMESTAMP_DELTA_MAX 2
 
@@ -124,7 +107,7 @@ static EVH storeLateRelease;
 /*
  * local variables
  */
-static Stack<StoreEntry*> LateReleaseStack;
+static std::stack<StoreEntry*> LateReleaseStack;
 MemAllocator *StoreEntry::pool = NULL;
 
 StorePointer Store::CurrentRoot = NULL;
@@ -144,7 +127,7 @@ Store::Root(StorePointer aRoot)
 void
 Store::Stats(StoreEntry * output)
 {
-    assert (output);
+    assert(output);
     Root().stat(*output);
 }
 
@@ -161,7 +144,7 @@ Store::sync()
 {}
 
 void
-Store::unlink (StoreEntry &anEntry)
+Store::unlink(StoreEntry &)
 {
     fatal("Store::unlink on invalid Store\n");
 }
@@ -169,7 +152,7 @@ Store::unlink (StoreEntry &anEntry)
 void *
 StoreEntry::operator new (size_t bytecount)
 {
-    assert (bytecount == sizeof (StoreEntry));
+    assert(bytecount == sizeof (StoreEntry));
 
     if (!pool) {
         pool = memPoolCreate ("StoreEntry", bytecount);
@@ -296,13 +279,13 @@ StoreEntry::bytesWanted (Range<size_t> const aRange, bool ignoreDelayPools) cons
 }
 
 bool
-StoreEntry::checkDeferRead(int fd) const
+StoreEntry::checkDeferRead(int) const
 {
     return (bytesWanted(Range<size_t>(0,INT_MAX)) == 0);
 }
 
 void
-StoreEntry::setNoDelay (bool const newValue)
+StoreEntry::setNoDelay(bool const newValue)
 {
     if (mem_obj)
         mem_obj->setNoDelay(newValue);
@@ -375,21 +358,21 @@ StoreEntry::storeClientType() const
 }
 
 StoreEntry::StoreEntry() :
-        mem_obj(NULL),
-        timestamp(-1),
-        lastref(-1),
-        expires(-1),
-        lastmod(-1),
-        swap_file_sz(0),
-        refcount(0),
-        flags(0),
-        swap_filen(-1),
-        swap_dirn(-1),
-        mem_status(NOT_IN_MEMORY),
-        ping_status(PING_NONE),
-        store_status(STORE_PENDING),
-        swap_status(SWAPOUT_NONE),
-        lock_count(0)
+    mem_obj(NULL),
+    timestamp(-1),
+    lastref(-1),
+    expires(-1),
+    lastmod(-1),
+    swap_file_sz(0),
+    refcount(0),
+    flags(0),
+    swap_filen(-1),
+    swap_dirn(-1),
+    mem_status(NOT_IN_MEMORY),
+    ping_status(PING_NONE),
+    store_status(STORE_PENDING),
+    swap_status(SWAPOUT_NONE),
+    lock_count(0)
 {
     debugs(20, 5, "StoreEntry constructed, this=" << this);
 }
@@ -914,6 +897,7 @@ struct _store_check_cachable_hist {
         int private_key;
         int too_many_open_files;
         int too_many_open_fds;
+        int missing_parts;
     } no;
 
     struct {
@@ -940,7 +924,7 @@ StoreEntry::checkTooSmall()
         return 0;
 
     if (STORE_OK == store_status)
-        if (mem_obj->object_sz < 0 ||
+        if (mem_obj->object_sz >= 0 &&
                 mem_obj->object_sz < Config.Store.minObjectSize)
             return 1;
     if (getReply()->content_length > -1)
@@ -949,6 +933,18 @@ StoreEntry::checkTooSmall()
     return 0;
 }
 
+bool
+StoreEntry::checkTooBig() const
+{
+    if (mem_obj->endOffset() > store_maxobjsize)
+        return true;
+
+    if (getReply()->content_length < 0)
+        return false;
+
+    return (getReply()->content_length > store_maxobjsize);
+}
+
 // TODO: move "too many open..." checks outside -- we are called too early/late
 bool
 StoreEntry::checkCachable()
@@ -980,9 +976,12 @@ StoreEntry::checkCachable()
             debugs(20, 3, "StoreEntry::checkCachable: NO: negative cached");
             ++store_check_cachable_hist.no.negative_cached;
             return 0;           /* avoid release call below */
-        } else if ((getReply()->content_length > 0 &&
-                    getReply()->content_length > store_maxobjsize) ||
-                   mem_obj->endOffset() > store_maxobjsize) {
+        } else if (!mem_obj || !getReply()) {
+            // XXX: In bug 4131, we forgetHit() without mem_obj, so we need
+            // this segfault protection, but how can we get such a HIT?
+            debugs(20, 2, "StoreEntry::checkCachable: NO: missing parts: " << *this);
+            ++store_check_cachable_hist.no.missing_parts;
+        } else if (checkTooBig()) {
             debugs(20, 2, "StoreEntry::checkCachable: NO: too big");
             ++store_check_cachable_hist.no.too_big;
         } else if (checkTooSmall()) {
@@ -1030,6 +1029,8 @@ storeCheckCachableStats(StoreEntry *sentry)
                       store_check_cachable_hist.no.wrong_content_length);
     storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
                       store_check_cachable_hist.no.negative_cached);
+    storeAppendPrintf(sentry, "no.missing_parts\t%d\n",
+                      store_check_cachable_hist.no.missing_parts);
     storeAppendPrintf(sentry, "no.too_big\t%d\n",
                       store_check_cachable_hist.no.too_big);
     storeAppendPrintf(sentry, "no.too_small\t%d\n",
@@ -1089,7 +1090,7 @@ StoreEntry::complete()
 
 /*
  * Someone wants to abort this transfer.  Set the reason in the
- * request structure, call the server-side callback and mark the
+ * request structure, call the callback and mark the
  * entry for releasing
  */
 void
@@ -1194,7 +1195,7 @@ storeGetMemSpace(int size)
  * it becomes active will self register
  */
 void
-Store::Maintain(void *notused)
+Store::Maintain(void *)
 {
     Store::Root().maintain();
 
@@ -1261,7 +1262,7 @@ StoreEntry::release()
             // lock the entry until rebuilding is done
             lock("storeLateRelease");
             setReleaseFlag();
-            LateReleaseStack.push_back(this);
+            LateReleaseStack.push(this);
         } else {
             destroyStoreEntry(static_cast<hash_link *>(this));
             // "this" is no longer valid
@@ -1286,10 +1287,9 @@ StoreEntry::release()
 }
 
 static void
-storeLateRelease(void *unused)
+storeLateRelease(void *)
 {
     StoreEntry *e;
-    int i;
     static int n = 0;
 
     if (StoreController::store_dirs_rebuilding) {
@@ -1297,13 +1297,14 @@ storeLateRelease(void *unused)
         return;
     }
 
-    for (i = 0; i < 10; ++i) {
-        e = LateReleaseStack.count ? LateReleaseStack.pop() : NULL;
-
-        if (e == NULL) {
-            /* done! */
+    // TODO: this works but looks unelegant.
+    for (int i = 0; i < 10; ++i) {
+        if (LateReleaseStack.empty()) {
             debugs(20, DBG_IMPORTANT, "storeLateRelease: released " << n << " objects");
             return;
+        } else {
+            e = LateReleaseStack.top();
+            LateReleaseStack.pop();
         }
 
         e->unlock("storeLateRelease");
@@ -1536,15 +1537,20 @@ StoreEntry::validToSend() const
     if (!mem_obj) // not backed by a memory cache and not collapsed
         return 0;
 
-    if (mem_obj->memCache.index >= 0) // backed by a shared memory cache
-        return 1;
-
     // StoreEntry::storeClientType() assumes DISK_CLIENT here, but there is no
-    // disk cache backing so we should not rely on the store cache at all. This
-    // is wrong for range requests that could feed off nibbled memory (XXX).
-    if (mem_obj->inmem_lo) // in local memory cache, but got nibbled at
+    // disk cache backing that store_client constructor will assert. XXX: This
+    // is wrong for range requests (that could feed off nibbled memory) and for
+    // entries backed by the shared memory cache (that could, in theory, get
+    // nibbled bytes from that cache, but there is no such "memoryIn" code).
+    if (mem_obj->inmem_lo) // in memory cache, but got nibbled at
         return 0;
 
+    // The following check is correct but useless at this position. TODO: Move
+    // it up when the shared memory cache can either replenish locally nibbled
+    // bytes or, better, does not use local RAM copy at all.
+    // if (mem_obj->memCache.index >= 0) // backed by a shared memory cache
+    //    return 1;
+
     return 1;
 }
 
@@ -1685,9 +1691,7 @@ StoreEntry::setMemStatus(mem_status_t new_status)
 const char *
 StoreEntry::url() const
 {
-    if (this == NULL)
-        return "[null_entry]";
-    else if (mem_obj == NULL)
+    if (mem_obj == NULL)
         return "[null_mem_obj]";
     else
         return mem_obj->storeId();
@@ -1840,6 +1844,19 @@ storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
 
 #endif
 
+void
+StoreEntry::storeErrorResponse(HttpReply *reply)
+{
+    lock("StoreEntry::storeErrorResponse");
+    buffer();
+    replaceHttpReply(reply);
+    flush();
+    complete();
+    negativeCache();
+    releaseRequest();
+    unlock("StoreEntry::storeErrorResponse");
+}
+
 /*
  * Replace a store entry with
  * a new reply. This eats the reply.
@@ -1897,6 +1914,40 @@ StoreEntry::getSerialisedMetaData()
     return result;
 }
 
+/**
+ * Abandon the transient entry our worker has created if neither the shared
+ * memory cache nor the disk cache wants to store it. Collapsed requests, if
+ * any, should notice and use Plan B instead of getting stuck waiting for us
+ * to start swapping the entry out.
+ */
+void
+StoreEntry::transientsAbandonmentCheck()
+{
+    if (mem_obj && !mem_obj->smpCollapsed && // this worker is responsible
+            mem_obj->xitTable.index >= 0 && // other workers may be interested
+            mem_obj->memCache.index < 0 && // rejected by the shared memory cache
+            mem_obj->swapout.decision == MemObject::SwapOut::swImpossible) {
+        debugs(20, 7, "cannot be shared: " << *this);
+        if (!shutting_down) // Store::Root() is FATALly missing during shutdown
+            Store::Root().transientsAbandon(*this);
+    }
+}
+
+void
+StoreEntry::memOutDecision(const bool)
+{
+    transientsAbandonmentCheck();
+}
+
+void
+StoreEntry::swapOutDecision(const MemObject::SwapOut::Decision &decision)
+{
+    // Abandon our transient entry if neither shared memory nor disk wants it.
+    assert(mem_obj);
+    mem_obj->swapout.decision = decision;
+    transientsAbandonmentCheck();
+}
+
 void
 StoreEntry::trimMemory(const bool preserveSwappable)
 {
@@ -2122,3 +2173,4 @@ NullStoreEntry::getSerialisedMetaData()
 {
     return NULL;
 }
+