File indexing completed on 2024-05-19 03:56:19

0001 /*
0002  *    This file is part of the KDE project.
0003  *
0004  *    SPDX-FileCopyrightText: 2010 Michael Pyne <mpyne@kde.org>
0005  *    SPDX-License-Identifier: LGPL-2.0-only
0006  */
0007 
0008 #ifndef KSDCMEMORY_P_H
0009 #define KSDCMEMORY_P_H
0010 
0011 #include "kcoreaddons_debug.h"
0012 #include "ksdclock_p.h"
0013 #include "kshareddatacache.h"
0014 
0015 /**
0016  * A very simple class whose only purpose is to be thrown as an exception from
0017  * underlying code to indicate that the shared cache is apparently corrupt.
0018  * This must be caught by top-level library code and used to unlink the cache
0019  * in this circumstance.
0020  *
0021  * @internal
0022  */
0023 class KSDCCorrupted
0024 {
0025 public:
0026     KSDCCorrupted()
0027     {
0028         qCWarning(KCOREADDONS_DEBUG) << "Error detected in cache!";
0029     }
0030 
0031     KSDCCorrupted(const QString message)
0032     {
0033         qCWarning(KCOREADDONS_DEBUG).noquote() << message;
0034     }
0035 
0036     KSDCCorrupted(const char *message)
0037     {
0038         KSDCCorrupted(QLatin1String(message));
0039     }
0040 };
0041 
0042 typedef qint32 pageID;
0043 
0044 // =========================================================================
0045 // Description of the cache:
0046 //
0047 // The shared memory cache is designed to be handled as two separate objects,
0048 // all contained in the same global memory segment. First off, there is the
0049 // basic header data, consisting of the global header followed by the
0050 // accounting data necessary to hold items (described in more detail
0051 // momentarily). Following the accounting data is the start of the "page table"
0052 // (essentially just as you'd see it in an Operating Systems text).
0053 //
0054 // The page table contains shared memory split into fixed-size pages, with a
0055 // configurable page size. In the event that the data is too large to fit into
0056 // a single logical page, it will need to occupy consecutive pages of memory.
0057 //
0058 // The accounting data that was referenced earlier is split into two:
0059 //
0060 // 1. index table, containing a fixed-size list of possible cache entries.
0061 // Each index entry is of type IndexTableEntry (below), and holds the various
0062 // accounting data and a pointer to the first page.
0063 //
0064 // 2. page table, which is used to speed up the process of searching for
0065 // free pages of memory. There is one entry for every page in the page table,
0066 // and it contains the index of the one entry in the index table actually
0067 // holding the page (or <0 if the page is free).
0068 //
0069 // The entire segment looks like so:
0070 // ?════════?═════════════?════════════?═══════?═══════?═══════?═══════?═══?
0071 // ? Header │ Index Table │ Page Table ? Pages │       │       │       │...?
0072 // ?════════?═════════════?════════════?═══════?═══════?═══════?═══════?═══?
0073 // =========================================================================
0074 
0075 // All elements of this struct must be "plain old data" (POD) types since it
0076 // will be in shared memory.  In addition, no pointers!  To point to something
0077 // you must use relative offsets since the pointer start addresses will be
0078 // different in each process.
0079 struct IndexTableEntry {
0080     uint fileNameHash;
0081     uint totalItemSize; // in bytes
0082     mutable uint useCount;
0083     time_t addTime;
0084     mutable time_t lastUsedTime;
0085     pageID firstPage;
0086 };
0087 
0088 // Page table entry
0089 struct PageTableEntry {
0090     // int so we can use values <0 for unassigned pages.
0091     qint32 index;
0092 };
0093 
0094 // Each individual page contains the cached data. The first page starts off with
0095 // the utf8-encoded key, a null '\0', and then the data follows immediately
0096 // from the next byte, possibly crossing consecutive page boundaries to hold
0097 // all of the data.
0098 // There is, however, no specific struct for a page, it is simply a location in
0099 // memory.
0100 
0101 // This is effectively the layout of the shared memory segment. The variables
0102 // contained within form the header, data contained afterwards is pointed to
0103 // by using special accessor functions.
0104 struct SharedMemory {
0105     /**
0106      * Note to downstream packagers: This version flag is intended to be
0107      * machine-specific. The KDE-provided source code will not set the lower
0108      * two bits to allow for distribution-specific needs, with the exception
0109      * of version 1 which was already defined in KDE Platform 4.5.
0110      * e.g. the next version bump will be from 4 to 8, then 12, etc.
0111      */
0112     enum {
0113         PIXMAP_CACHE_VERSION = 12,
0114         MINIMUM_CACHE_SIZE = 4096,
0115     };
0116 
0117     /// The maximum number of probes to make while searching for a bucket in
0118     /// the presence of collisions in the cache index table.
0119     static const uint MAX_PROBE_COUNT = 6;
0120 
0121     // Note to those who follow me. You should not, under any circumstances, ever
0122     // re-arrange the following two fields, even if you change the version number
0123     // for later revisions of this code.
0124     QAtomicInt ready; ///< DO NOT INITIALIZE
0125     quint8 version;
0126 
0127     // See kshareddatacache_p.h
0128     SharedLock shmLock;
0129 
0130     uint cacheSize;
0131     uint cacheAvail;
0132     QAtomicInt evictionPolicy;
0133 
0134     // pageSize and cacheSize determine the number of pages. The number of
0135     // pages determine the page table size and (indirectly) the index table
0136     // size.
0137     QAtomicInt pageSize;
0138 
0139     // This variable is added to reserve space for later cache timestamping
0140     // support. The idea is this variable will be updated when the cache is
0141     // written to, to allow clients to detect a changed cache quickly.
0142     QAtomicInt cacheTimestamp;
0143 
0144     /**
0145      * Converts the given average item size into an appropriate page size.
0146      */
0147     static unsigned equivalentPageSize(unsigned itemSize);
0148 
0149     // Returns pageSize in unsigned format.
0150     unsigned cachePageSize() const;
0151 
0152     /**
0153      * This is effectively the class ctor.  But since we're in shared memory,
0154      * there's a few rules:
0155      *
0156      * 1. To allow for some form of locking in the initial-setup case, we
0157      * use an atomic int, which will be initialized to 0 by mmap().  Then to
0158      * take the lock we atomically increment the 0 to 1.  If we end up calling
0159      * the QAtomicInt constructor we can mess that up, so we can't use a
0160      * constructor for this class either.
0161      * 2. Any member variable you add takes up space in shared memory as well,
0162      * so make sure you need it.
0163      */
0164     bool performInitialSetup(uint _cacheSize, uint _pageSize);
0165 
0166     void clearInternalTables();
0167     const IndexTableEntry *indexTable() const;
0168     const PageTableEntry *pageTable() const;
0169     const void *cachePages() const;
0170     const void *page(pageID at) const;
0171 
0172     // The following are non-const versions of some of the methods defined
0173     // above.  They use const_cast<> because I feel that is better than
0174     // duplicating the code.  I suppose template member functions (?)
0175     // may work, may investigate later.
0176     IndexTableEntry *indexTable();
0177     PageTableEntry *pageTable();
0178     void *cachePages();
0179     void *page(pageID at);
0180     uint pageTableSize() const;
0181     uint indexTableSize() const;
0182 
0183     /**
0184      * @return the index of the first page, for the set of contiguous
0185      * pages that can hold @p pagesNeeded PAGES.
0186      */
0187     pageID findEmptyPages(uint pagesNeeded) const;
0188 
0189     // left < right?
0190     static bool lruCompare(const IndexTableEntry &l, const IndexTableEntry &r);
0191 
0192     // left < right?
0193     static bool seldomUsedCompare(const IndexTableEntry &l, const IndexTableEntry &r);
0194 
0195     // left < right?
0196     static bool ageCompare(const IndexTableEntry &l, const IndexTableEntry &r);
0197 
0198     void defragment();
0199 
0200     /**
0201      * Finds the index entry for a given key.
0202      * @param key UTF-8 encoded key to search for.
0203      * @return The index of the entry in the cache named by @p key. Returns
0204      *         <0 if no such entry is present.
0205      */
0206     qint32 findNamedEntry(const QByteArray &key) const;
0207 
0208     // Function to use with std::unique_ptr in removeUsedPages below...
0209     static void deleteTable(IndexTableEntry *table);
0210 
0211     /**
0212      * Removes the requested number of pages.
0213      *
0214      * @param numberNeeded the number of pages required to fulfill a current request.
0215      *        This number should be <0 and <= the number of pages in the cache.
0216      * @return The identifier of the beginning of a consecutive block of pages able
0217      *         to fill the request. Returns a value >= pageTableSize() if no such
0218      *         request can be filled.
0219      * @internal
0220      */
0221     uint removeUsedPages(uint numberNeeded);
0222 
0223     // Returns the total size required for a given cache size.
0224     static uint totalSize(uint cacheSize, uint effectivePageSize);
0225 
0226     uint fileNameHash(const QByteArray &utf8FileName) const;
0227     void clear();
0228     void removeEntry(uint index);
0229 
0230     static quint32 generateHash(const QByteArray &buffer);
0231 
0232     /**
0233      * @return the smallest integer greater than or equal to (@p a / @p b).
0234      * @param a Numerator, should be ≥ 0.
0235      * @param b Denominator, should be > 0.
0236      */
0237     static unsigned intCeil(unsigned a, unsigned b);
0238 };
0239 
0240 #endif /* KSDCMEMORY_P_H */