]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/Store.h
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / Store.h
index a1f6ef97208488df262b008d661b107ce7aac672..4d7dbeff47776e4a59e241e0ef0e26cec27fe9da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -45,36 +45,41 @@ public:
     static DeferredRead::DeferrableRead DeferReader;
     bool checkDeferRead(int fd) const;
 
-    virtual const char *getMD5Text() const;
+    const char *getMD5Text() const;
     StoreEntry();
     virtual ~StoreEntry();
 
-    virtual HttpReply const *getReply() const;
-    virtual void write (StoreIOBuffer);
+    MemObject &mem() { assert(mem_obj); return *mem_obj; }
+    const MemObject &mem() const { assert(mem_obj); return *mem_obj; }
+
+    /// \retval * the address of freshest reply (if mem_obj exists)
+    /// \retval nullptr when mem_obj does not exist
+    /// \see MemObject::freshestReply()
+    const HttpReply *hasFreshestReply() const { return mem_obj ? &mem_obj->freshestReply() : nullptr; }
 
-    /** Check if the Store entry is emtpty
+    void write(StoreIOBuffer);
+
+    /** Check if the Store entry is empty
      * \retval true   Store contains 0 bytes of data.
      * \retval false  Store contains 1 or more bytes of data.
      * \retval false  Store contains negative content !!!!!!
      */
-    virtual bool isEmpty() const {
-        assert (mem_obj);
-        return mem_obj->endOffset() == 0;
-    }
-    virtual bool isAccepting() const;
-    virtual size_t bytesWanted(Range<size_t> const aRange, bool ignoreDelayPool = false) const;
+    bool isEmpty() const { return mem().endOffset() == 0; }
+    bool isAccepting() const;
+    size_t bytesWanted(Range<size_t> const aRange, bool ignoreDelayPool = false) const;
     /// flags [truncated or too big] entry with ENTRY_BAD_LENGTH and releases it
     void lengthWentBad(const char *reason);
-    virtual void complete();
-    virtual store_client_t storeClientType() const;
-    virtual char const *getSerialisedMetaData();
+    void complete();
+    store_client_t storeClientType() const;
+    /// \returns a malloc()ed buffer containing a length-long packed swap header
+    const char *getSerialisedMetaData(size_t &length) const;
     /// Store a prepared error response. MemObject locks the reply object.
     void storeErrorResponse(HttpReply *reply);
-    void replaceHttpReply(HttpReply *, bool andStartWriting = true);
+    void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting = true);
     void startWriting(); ///< pack and write reply headers and, maybe, body
     /// whether we may start writing to disk (now or in the future)
-    virtual bool mayStartSwapOut();
-    virtual void trimMemory(const bool preserveSwappable);
+    bool mayStartSwapOut();
+    void trimMemory(const bool preserveSwappable);
 
     // called when a decision to cache in memory has been made
     void memOutDecision(const bool willCacheInRam);
@@ -82,28 +87,45 @@ public:
     void swapOutDecision(const MemObject::SwapOut::Decision &decision);
 
     void abort();
-    void makePublic(const KeyScope keyScope = ksDefault);
+    bool makePublic(const KeyScope keyScope = ksDefault);
     void makePrivate(const bool shareable);
     /// A low-level method just resetting "private key" flags.
     /// To avoid key inconsistency please use forcePublicKey()
     /// or similar instead.
     void clearPrivate();
-    void setPublicKey(const KeyScope keyScope = ksDefault);
+    bool setPublicKey(const KeyScope keyScope = ksDefault);
     /// Resets existing public key to a public key with default scope,
     /// releasing the old default-scope entry (if any).
     /// Does nothing if the existing public key already has default scope.
     void clearPublicKeyScope();
-    void setPrivateKey(const bool shareable);
+
+    /// \returns public key (if the entry has it) or nil (otherwise)
+    const cache_key *publicKey() const {
+        return (!EBIT_TEST(flags, KEY_PRIVATE)) ?
+               reinterpret_cast<const cache_key*>(key): // may be nil
+               nullptr;
+    }
+
+    /// Either fills this entry with private key or changes the existing key
+    /// from public to private.
+    /// \param permanent whether this entry should be private forever.
+    void setPrivateKey(const bool shareable, const bool permanent);
+
     void expireNow();
+    /// Makes the StoreEntry private and marks the corresponding entry
+    /// for eventual removal from the Store.
     void releaseRequest(const bool shareable = false);
     void negativeCache();
-    void cacheNegatively();     /** \todo argh, why both? */
+    bool cacheNegatively();     /** \todo argh, why both? */
     void invokeHandlers();
-    void purgeMem();
     void cacheInMemory(); ///< start or continue storing in memory cache
     void swapOut();
     /// whether we are in the process of writing this entry to disk
     bool swappingOut() const { return swap_status == SWAPOUT_WRITING; }
+    /// whether the entire entry is now on disk (possibly marked for deletion)
+    bool swappedOut() const { return swap_status == SWAPOUT_DONE; }
+    /// whether we failed to write this entry to disk
+    bool swapoutFailed() const { return swap_status == SWAPOUT_FAILED; }
     void swapOutFileClose(int how);
     const char *url() const;
     /// Satisfies cachability requirements shared among disk and RAM caches.
@@ -111,7 +133,7 @@ public:
     /// TODO: Rename and make private so only those two methods can call this.
     bool checkCachable();
     int checkNegativeHit() const;
-    int locked() const;
+    int locked() const { return lock_count; }
     int validToSend() const;
     bool memoryCachable(); ///< checkCachable() and can be cached in memory
 
@@ -156,8 +178,33 @@ public:
     /// whether this entry has an ETag; if yes, puts ETag value into parameter
     bool hasEtag(ETag &etag) const;
 
+    /// Updates easily-accessible non-Store-specific parts of the entry.
+    /// Use Controller::updateOnNotModified() instead of this helper.
+    /// \returns whether anything was actually updated
+    bool updateOnNotModified(const StoreEntry &e304);
+
     /// the disk this entry is [being] cached on; asserts for entries w/o a disk
     Store::Disk &disk() const;
+    /// whether one of this StoreEntry owners has locked the corresponding
+    /// disk entry (at the specified disk entry coordinates, if any)
+    bool hasDisk(const sdirno dirn = -1, const sfileno filen = -1) const;
+    /// Makes hasDisk(dirn, filn) true. The caller should have locked
+    /// the corresponding disk store entry for reading or writing.
+    void attachToDisk(const sdirno, const sfileno, const swap_status_t);
+    /// Makes hasDisk() false. The caller should have unlocked
+    /// the corresponding disk store entry.
+    void detachFromDisk();
+
+    /// whether there is a corresponding locked transients table entry
+    bool hasTransients() const { return mem_obj && mem_obj->xitTable.index >= 0; }
+    /// whether there is a corresponding locked shared memory table entry
+    bool hasMemStore() const { return mem_obj && mem_obj->memCache.index >= 0; }
+
+    /// whether this entry can feed collapsed requests and only them
+    bool hittingRequiresCollapsing() const { return EBIT_TEST(flags, ENTRY_REQUIRES_COLLAPSING); }
+
+    /// allow or forbid collapsed requests feeding
+    void setCollapsingRequirement(const bool required);
 
     MemObject *mem_obj;
     RemovalPolicyNode repl;
@@ -192,19 +239,14 @@ public:
     static void getPublicByRequest(StoreClient * aClient, HttpRequest * request);
     static void getPublic(StoreClient * aClient, const char *uri, const HttpRequestMethod& method);
 
-    virtual bool isNull() {
-        return false;
-    };
-
     void *operator new(size_t byteCount);
     void operator delete(void *address);
-    void setReleaseFlag();
 #if USE_SQUID_ESI
 
     ESIElement::Pointer cachedESITree;
 #endif
-    virtual int64_t objectLen() const;
-    virtual int64_t contentLen() const;
+    int64_t objectLen() const { return mem().object_sz; }
+    int64_t contentLen() const { return objectLen() - mem().baseReply().hdr_sz; }
 
     /// claim shared ownership of this entry (for use in a given context)
     /// matching lock() and unlock() contexts eases leak triage but is optional
@@ -220,7 +262,17 @@ public:
     /// update last reference timestamp and related Store metadata
     void touch();
 
-    virtual void release(const bool shareable = false);
+    /// One of the three methods to get rid of an unlocked StoreEntry object.
+    /// Removes all unlocked (and marks for eventual removal all locked) Store
+    /// entries, including attached and unattached entries that have our key.
+    /// Also destroys us if we are unlocked or makes us private otherwise.
+    void release(const bool shareable = false);
+
+    /// One of the three methods to get rid of an unlocked StoreEntry object.
+    /// May destroy this object if it is unlocked; does nothing otherwise.
+    /// Unlike release(), may not trigger eviction of underlying store entries,
+    /// but, unlike destroyStoreEntry(), does honor an earlier release request.
+    void abandon(const char *context) { if (!locked()) doAbandon(context); }
 
     /// May the caller commit to treating this [previously locked]
     /// entry as a cache hit?
@@ -242,12 +294,17 @@ public:
     virtual void flush();
 
 protected:
+    typedef Store::EntryGuard EntryGuard;
+
     void transientsAbandonmentCheck();
+    /// does nothing except throwing if disk-associated data members are inconsistent
+    void checkDisk() const;
 
 private:
+    void doAbandon(const char *context);
     bool checkTooBig() const;
     void forcePublicKey(const cache_key *newkey);
-    void adjustVary();
+    StoreEntry *adjustVary();
     const cache_key *calcPublicKey(const KeyScope keyScope);
 
     static MemAllocator *pool;
@@ -275,44 +332,56 @@ private:
 std::ostream &operator <<(std::ostream &os, const StoreEntry &e);
 
 /// \ingroup StoreAPI
-class NullStoreEntry:public StoreEntry
-{
+typedef void (*STOREGETCLIENT) (StoreEntry *, void *cbdata);
 
+namespace Store {
+
+/// a smart pointer similar to std::unique_ptr<> that automatically
+/// release()s and unlock()s the guarded Entry on stack-unwinding failures
+class EntryGuard {
 public:
-    static NullStoreEntry *getInstance();
-    bool isNull() {
-        return true;
+    /// \param entry either nil or a locked Entry to manage
+    /// \param context default unlock() message
+    EntryGuard(Entry *entry, const char *context):
+        entry_(entry), context_(context) {
+        assert(!entry_ || entry_->locked());
     }
 
-    const char *getMD5Text() const;
-    HttpReply const *getReply() const { return NULL; }
-    void write (StoreIOBuffer) {}
+    ~EntryGuard() {
+        if (entry_) {
+            // something went wrong -- the caller did not unlockAndReset() us
+            onException();
+        }
+    }
 
-    bool isEmpty () const {return true;}
+    EntryGuard(EntryGuard &&) = delete; // no copying or moving (for now)
 
-    virtual size_t bytesWanted(Range<size_t> const aRange, bool) const { return aRange.end; }
+    /// like std::unique_ptr::get()
+    /// \returns nil or the guarded (locked) entry
+    Entry *get() {
+        return entry_;
+    }
 
-    void operator delete(void *address);
-    void complete() {}
+    /// like std::unique_ptr::reset()
+    /// stops guarding the entry
+    /// unlocks the entry (which may destroy it)
+    void unlockAndReset(const char *resetContext = nullptr) {
+        if (entry_) {
+            entry_->unlock(resetContext ? resetContext : context_);
+            entry_ = nullptr;
+        }
+    }
 
 private:
-    store_client_t storeClientType() const {return STORE_MEM_CLIENT;}
-
-    char const *getSerialisedMetaData();
-    virtual bool mayStartSwapOut() { return false; }
+    void onException() noexcept;
 
-    void trimMemory(const bool) {}
-
-    static NullStoreEntry _instance;
+    Entry *entry_; ///< the guarded Entry or nil
+    const char *context_; ///< default unlock() message
 };
 
-/// \ingroup StoreAPI
-typedef void (*STOREGETCLIENT) (StoreEntry *, void *cbdata);
-
-namespace Store {
 void Stats(StoreEntry *output);
 void Maintain(void *unused);
-};
+}; // namespace Store
 
 /// \ingroup StoreAPI
 size_t storeEntryInUse();
@@ -338,7 +407,7 @@ StoreEntry *storeCreateEntry(const char *, const char *, const RequestFlags &, c
 
 /// \ingroup StoreAPI
 /// Creates a new StoreEntry with mem_obj and sets initial flags/states.
-StoreEntry *storeCreatePureEntry(const char *storeId, const char *logUrl, const RequestFlags &, const HttpRequestMethod&);
+StoreEntry *storeCreatePureEntry(const char *storeId, const char *logUrl, const HttpRequestMethod&);
 
 /// \ingroup StoreAPI
 void storeInit(void);
@@ -376,7 +445,10 @@ void storeFsDone(void);
 /// \ingroup StoreAPI
 void storeReplAdd(const char *, REMOVALPOLICYCREATE *);
 
-/// \ingroup StoreAPI
+/// One of the three methods to get rid of an unlocked StoreEntry object.
+/// This low-level method ignores lock()ing and release() promises. It never
+/// leaves the entry in the local store_table.
+/// TODO: Hide by moving its functionality into the StoreEntry destructor.
 extern FREE destroyStoreEntry;
 
 /// \ingroup StoreAPI