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 */