]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/Transients.cc
NoNewGlobals for MapLabel (#1746)
[thirdparty/squid.git] / src / Transients.cc
index cc49f99191105bac75dc0d63a11ea9589a7d833e..0eab8f767ad1e5e726a92cfebce75880fe1068d0 100644 (file)
@@ -1,8 +1,13 @@
 /*
- * DEBUG: section 20    Storage Manager
+ * 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 20    Storage Manager */
+
 #include "squid.h"
 #include "base/RunnersRegistry.h"
 #include "CollapsedForwarding.h"
 #include "tools.h"
 #include "Transients.h"
 
-#if HAVE_LIMITS_H
 #include <limits>
-#endif
 
-/// shared memory segment path to use for Transients maps
-static const char *MapLabel = "transients_map";
+/// shared memory segment path to use for Transients map
+static const auto &
+MapLabel()
+{
+    static const auto label = new SBuf("transients_map");
+    return *label;
+}
 
-Transients::Transients(): map(NULL), locals(NULL)
+Transients::Transients(): map(nullptr), locals(nullptr)
 {
 }
 
@@ -37,15 +45,16 @@ Transients::~Transients()
 void
 Transients::init()
 {
+    assert(Enabled());
     const int64_t entryLimit = EntryLimit();
-    if (entryLimit <= 0)
-        return; // no SMP support or a misconfiguration
+    assert(entryLimit > 0);
 
     Must(!map);
-    map = new TransientsMap(MapLabel);
+    map = new TransientsMap(MapLabel());
     map->cleaner = this;
+    map->disableHitValidation(); // Transients lacks slices to validate
 
-    locals = new Locals(entryLimit, 0);
+    locals = new Locals(entryLimit, nullptr);
 }
 
 void
@@ -60,6 +69,8 @@ Transients::getStats(StoreInfoStats &stats) const
     stats.mem.size =
         Ipc::Mem::PageLevel(Ipc::Mem::PageId::cachePage) * pageSize;
     stats.mem.count = currentCount();
+#else
+    (void)stats;
 #endif
 }
 
@@ -130,35 +141,22 @@ Transients::reference(StoreEntry &)
 }
 
 bool
-Transients::dereference(StoreEntry &, bool)
+Transients::dereference(StoreEntry &)
 {
     // no need to keep e in the global store_table for us; we have our own map
     return false;
 }
 
-int
-Transients::callback()
-{
-    return 0;
-}
-
-StoreSearch *
-Transients::search(String const, HttpRequest *)
-{
-    fatal("not implemented");
-    return NULL;
-}
-
 StoreEntry *
 Transients::get(const cache_key *key)
 {
     if (!map)
-        return NULL;
+        return nullptr;
 
     sfileno index;
     const Ipc::StoreMapAnchor *anchor = map->openForReading(key, index);
     if (!anchor)
-        return NULL;
+        return nullptr;
 
     // If we already have a local entry, the store_table should have found it.
     // Since it did not, the local entry key must have changed from public to
@@ -167,179 +165,143 @@ Transients::get(const cache_key *key)
     if (StoreEntry *oldE = locals->at(index)) {
         debugs(20, 3, "not joining private " << *oldE);
         assert(EBIT_TEST(oldE->flags, KEY_PRIVATE));
-    } else if (StoreEntry *newE = copyFromShm(index)) {
-        return newE; // keep read lock to receive updates from others
+        map->closeForReadingAndFreeIdle(index);
+        return nullptr;
     }
 
-    // private entry or loading failure
-    map->closeForReading(index);
-    return NULL;
-}
+    StoreEntry *e = new StoreEntry();
+    e->createMemObject();
+    e->mem_obj->xitTable.open(index, Store::ioReading);
 
-StoreEntry *
-Transients::copyFromShm(const sfileno index)
-{
-    const TransientsMap::Extras &extras = map->extras(index);
-
-    // create a brand new store entry and initialize it with stored info
-    StoreEntry *e = storeCreatePureEntry(extras.url, extras.url,
-                                     extras.reqFlags, extras.reqMethod);
-
-    assert(e->mem_obj);
-    e->mem_obj->method = extras.reqMethod;
-    e->mem_obj->xitTable.io = MemObject::ioReading;
-    e->mem_obj->xitTable.index = index;
-
-    e->setPublicKey();
-    assert(e->key);
-
-    // How do we know its SMP- and not just locally-collapsed? A worker gets
-    // locally-collapsed entries from the local store_table, not Transients.
-    // TODO: Can we remove smpCollapsed by not syncing non-transient entries?
-    e->mem_obj->smpCollapsed = true;
-
-    assert(!locals->at(index));
-    // We do not lock e because we do not want to prevent its destruction;
-    // e is tied to us via mem_obj so we will know when it is destructed.
-    locals->at(index) = e;
+    // keep read lock to receive updates from others
     return e;
 }
 
-void
-Transients::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
-{
-    // XXX: not needed but Store parent forces us to implement this
-    fatal("Transients::get(key,callback,data) should not be called");
-}
-
 StoreEntry *
 Transients::findCollapsed(const sfileno index)
 {
     if (!map)
-        return NULL;
+        return nullptr;
 
     if (StoreEntry *oldE = locals->at(index)) {
-        debugs(20, 5, "found " << *oldE << " at " << index << " in " << MapLabel);
+        debugs(20, 5, "found " << *oldE << " at " << index << " in " << MapLabel());
         assert(oldE->mem_obj && oldE->mem_obj->xitTable.index == index);
         return oldE;
     }
 
-    debugs(20, 3, "no entry at " << index << " in " << MapLabel);
-    return NULL;
+    debugs(20, 3, "no entry at " << index << " in " << MapLabel());
+    return nullptr;
 }
 
 void
-Transients::startWriting(StoreEntry *e, const RequestFlags &reqFlags,
-                const HttpRequestMethod &reqMethod)
+Transients::monitorIo(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
 {
-    assert(e);
-    assert(e->mem_obj);
-    assert(e->mem_obj->xitTable.index < 0);
+    if (!e->hasTransients()) {
+        addEntry(e, key, direction);
+        assert(e->hasTransients());
+    }
 
-    if (!map) {
-        debugs(20, 5, "No map to add " << *e);
-        return;
-       }
+    const auto index = e->mem_obj->xitTable.index;
+    if (const auto old = locals->at(index)) {
+        assert(old == e);
+    } else {
+        // We do not lock e because we do not want to prevent its destruction;
+        // e is tied to us via mem_obj so we will know when it is destructed.
+        locals->at(index) = e;
+    }
+}
 
-    sfileno index = 0;
-    Ipc::StoreMapAnchor *slot = map->openForWriting(reinterpret_cast<const cache_key *>(e->key), index);
-    if (!slot) {
-        debugs(20, 5, "collision registering " << *e);
-        return;
-       }
-
-    try {
-        if (copyToShm(*e, index, reqFlags, reqMethod)) {
-            slot->set(*e);
-            e->mem_obj->xitTable.io = MemObject::ioWriting;
-            e->mem_obj->xitTable.index = index;
-            map->startAppending(index);
-            // keep write lock -- we will be supplying others with updates
-            return;
-               }
-        // fall through to the error handling code
-       } 
-    catch (const std::exception &x) { // TODO: should we catch ... as well?
-        debugs(20, 2, "error keeping entry " << index <<
-               ' ' << *e << ": " << x.what());
-        // fall through to the error handling code
-       }
-
-    map->abortWriting(index);
-}
-
-/// copies all relevant local data to shared memory
-bool
-Transients::copyToShm(const StoreEntry &e, const sfileno index,
-                      const RequestFlags &reqFlags,
-                      const HttpRequestMethod &reqMethod)
+/// creates a new Transients entry
+void
+Transients::addEntry(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
 {
-    TransientsMap::Extras &extras = map->extras(index);
-
-    const char *url = e.url();
-    const size_t urlLen = strlen(url);
-    Must(urlLen < sizeof(extras.url)); // we have space to store it all, plus 0
-    strncpy(extras.url, url, sizeof(extras.url));
-       extras.url[urlLen] = '\0';
+    assert(e);
+    assert(e->mem_obj);
+    assert(!e->hasTransients());
 
-    extras.reqFlags = reqFlags;
+    Must(map); // configured to track transients
 
-    Must(reqMethod != Http::METHOD_OTHER);
-    extras.reqMethod = reqMethod.id();
+    if (direction == Store::ioWriting)
+        return addWriterEntry(*e, key);
 
-    return true;
+    assert(direction == Store::ioReading);
+    addReaderEntry(*e, key);
 }
 
+/// addEntry() helper used for cache entry creators/writers
 void
-Transients::noteFreeMapSlice(const sfileno sliceId)
+Transients::addWriterEntry(StoreEntry &e, const cache_key *key)
 {
-    // TODO: we should probably find the entry being deleted and abort it
+    sfileno index = 0;
+    const auto anchor = map->openForWriting(key, index);
+    if (!anchor)
+        throw TextException("writer collision", Here());
+
+    // set ASAP in hope to unlock the slot if something throws
+    // and to provide index to such methods as hasWriter()
+    e.mem_obj->xitTable.open(index, Store::ioWriting);
+
+    anchor->setKey(key);
+    // allow reading and receive remote DELETE events, but do not switch to
+    // the reading lock because transientReaders() callers want true readers
+    map->startAppending(index);
 }
 
+/// addEntry() helper used for cache readers
+/// readers do not modify the cache, but they must create a Transients entry
 void
-Transients::abandon(const StoreEntry &e)
+Transients::addReaderEntry(StoreEntry &e, const cache_key *key)
 {
-    assert(e.mem_obj && map);
-    map->freeEntry(e.mem_obj->xitTable.index); // just marks the locked entry
-    CollapsedForwarding::Broadcast(e);
-    // We do not unlock the entry now because the problem is most likely with
-    // the server resource rather than a specific cache writer, so we want to
-    // prevent other readers from collapsing requests for that resource.
+    sfileno index = 0;
+    const auto anchor = map->openOrCreateForReading(key, index);
+    if (!anchor)
+        throw TextException("reader collision", Here());
+
+    e.mem_obj->xitTable.open(index, Store::ioReading);
+    // keep the entry locked (for reading) to receive remote DELETE events
 }
 
 bool
-Transients::abandoned(const StoreEntry &e) const
+Transients::hasWriter(const StoreEntry &e)
 {
-    assert(e.mem_obj);
-    return abandonedAt(e.mem_obj->xitTable.index);
+    if (!e.hasTransients())
+        return false;
+    return map->peekAtWriter(e.mem_obj->xitTable.index);
 }
 
-/// whether an in-transit entry at the index is now abandoned by its writer
-bool
-Transients::abandonedAt(const sfileno index) const
+void
+Transients::noteFreeMapSlice(const Ipc::StoreMapSliceId)
+{
+    // TODO: we should probably find the entry being deleted and abort it
+}
+
+void
+Transients::status(const StoreEntry &entry, Transients::EntryStatus &entryStatus) const
 {
     assert(map);
-    return map->readableEntry(index).waitingToBeFreed;
+    assert(entry.hasTransients());
+    const auto idx = entry.mem_obj->xitTable.index;
+    const auto &anchor = isWriter(entry) ?
+                         map->writeableEntry(idx) : map->readableEntry(idx);
+    entryStatus.hasWriter = anchor.writing();
+    entryStatus.waitingToBeFreed = anchor.waitingToBeFreed;
 }
 
 void
 Transients::completeWriting(const StoreEntry &e)
 {
-    if (e.mem_obj && e.mem_obj->xitTable.index >= 0) {
-        assert(e.mem_obj->xitTable.io == MemObject::ioWriting);
-        // there will be no more updates from us after this, so we must prevent
-        // future readers from joining
-        map->freeEntry(e.mem_obj->xitTable.index); // just marks the locked entry
-        map->closeForWriting(e.mem_obj->xitTable.index);
-        e.mem_obj->xitTable.index = -1;
-        e.mem_obj->xitTable.io = MemObject::ioDone;
-    }
+    debugs(20, 5, e);
+    assert(e.hasTransients());
+    assert(isWriter(e));
+    map->switchWritingToReading(e.mem_obj->xitTable.index);
+    e.mem_obj->xitTable.io = Store::ioReading;
+    CollapsedForwarding::Broadcast(e);
 }
 
 int
 Transients::readers(const StoreEntry &e) const
 {
-    if (e.mem_obj && e.mem_obj->xitTable.index >= 0) {
+    if (e.hasTransients()) {
         assert(map);
         return map->peekAtEntry(e.mem_obj->xitTable.index).lock.readers;
     }
@@ -347,26 +309,50 @@ Transients::readers(const StoreEntry &e) const
 }
 
 void
-Transients::markForUnlink(StoreEntry &e)
+Transients::evictCached(StoreEntry &e)
+{
+    debugs(20, 5, e);
+    if (e.hasTransients()) {
+        const auto index = e.mem_obj->xitTable.index;
+        if (map->freeEntry(index)) {
+            // Delay syncCollapsed(index) which may end `e` wait for updates.
+            // Calling it directly/here creates complex reentrant call chains.
+            CollapsedForwarding::Broadcast(e, true);
+        }
+    } // else nothing to do because e must be private
+}
+
+void
+Transients::evictIfFound(const cache_key *key)
 {
-    if (e.mem_obj && e.mem_obj->xitTable.io == MemObject::ioWriting)
-        abandon(e);
+    if (!map)
+        return;
+
+    const sfileno index = map->fileNoByKey(key);
+    if (map->freeEntry(index))
+        CollapsedForwarding::Broadcast(index, true);
 }
 
 void
-Transients::disconnect(MemObject &mem_obj)
+Transients::disconnect(StoreEntry &entry)
 {
-    if (mem_obj.xitTable.index >= 0) {
+    debugs(20, 5, entry);
+    if (entry.hasTransients()) {
+        auto &xitTable = entry.mem_obj->xitTable;
         assert(map);
-        if (mem_obj.xitTable.io == MemObject::ioWriting) {
-            map->abortWriting(mem_obj.xitTable.index);
+        if (isWriter(entry)) {
+            // completeWriting() was not called, so there could be an active
+            // Store writer out there, but we should not abortWriting() here
+            // because another writer may have succeeded, making readers happy.
+            // If none succeeded, the readers will notice the lack of writers.
+            map->closeForWriting(xitTable.index);
+            CollapsedForwarding::Broadcast(entry);
         } else {
-            assert(mem_obj.xitTable.io == MemObject::ioReading);
-            map->closeForReading(mem_obj.xitTable.index);
+            assert(isReader(entry));
+            map->closeForReadingAndFreeIdle(xitTable.index);
         }
-        locals->at(mem_obj.xitTable.index) = NULL;
-        mem_obj.xitTable.index = -1;
-        mem_obj.xitTable.io = MemObject::ioDone;
+        locals->at(xitTable.index) = nullptr;
+        xitTable.close();
     }
 }
 
@@ -374,11 +360,27 @@ Transients::disconnect(MemObject &mem_obj)
 int64_t
 Transients::EntryLimit()
 {
-    // TODO: we should also check whether any SMP-aware caching is configured
-    if (!UsingSmp() || !Config.onoff.collapsed_forwarding)
-        return 0; // no SMP collapsed forwarding possible or needed
+    return (UsingSmp() && Store::Controller::SmpAware()) ?
+           Config.shared_transient_entries_limit : 0;
+}
 
-    return 16*1024; // TODO: make configurable?
+bool
+Transients::markedForDeletion(const cache_key *key) const
+{
+    assert(map);
+    return map->markedForDeletion(key);
+}
+
+bool
+Transients::isReader(const StoreEntry &e) const
+{
+    return e.mem_obj && e.mem_obj->xitTable.io == Store::ioReading;
+}
+
+bool
+Transients::isWriter(const StoreEntry &e) const
+{
+    return e.mem_obj && e.mem_obj->xitTable.io == Store::ioWriting;
 }
 
 /// initializes shared memory segment used by Transients
@@ -386,41 +388,38 @@ class TransientsRr: public Ipc::Mem::RegisteredRunner
 {
 public:
     /* RegisteredRunner API */
-    TransientsRr(): mapOwner(NULL) {}
-    virtual void run(const RunnerRegistry &);
-    virtual ~TransientsRr();
+    void useConfig() override;
+    ~TransientsRr() override;
 
 protected:
-    virtual void create(const RunnerRegistry &);
+    void create() override;
 
 private:
-    TransientsMap::Owner *mapOwner;
+    TransientsMap::Owner *mapOwner = nullptr;
 };
 
-RunnerRegistrationEntry(rrAfterConfig, TransientsRr);
+DefineRunnerRegistrator(TransientsRr);
 
 void
-TransientsRr::run(const RunnerRegistry &r)
+TransientsRr::useConfig()
 {
     assert(Config.memShared.configured());
-    Ipc::Mem::RegisteredRunner::run(r);
+    Ipc::Mem::RegisteredRunner::useConfig();
 }
 
 void
-TransientsRr::create(const RunnerRegistry &)
+TransientsRr::create()
 {
-    if (!Config.onoff.collapsed_forwarding)
-        return;
-
     const int64_t entryLimit = Transients::EntryLimit();
     if (entryLimit <= 0)
         return; // no SMP configured or a misconfiguration
 
     Must(!mapOwner);
-    mapOwner = TransientsMap::Init(MapLabel, entryLimit);
+    mapOwner = TransientsMap::Init(MapLabel(), entryLimit);
 }
 
 TransientsRr::~TransientsRr()
 {
     delete mapOwner;
 }
+