KDECore
kshareddatacache.cpp
Go to the documentation of this file.
00001 /* 00002 * This file is part of the KDE project. 00003 * Copyright © 2010, 2012 Michael Pyne <mpyne@kde.org> 00004 * Copyright © 2012 Ralf Jung <ralfjung-e@gmx.de> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License version 2 as published by the Free Software Foundation. 00009 * 00010 * This library includes "MurmurHash" code from Austin Appleby, which is 00011 * placed in the public domain. See http://sites.google.com/site/murmurhash/ 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Library General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Library General Public License 00019 * along with this library; see the file COPYING.LIB. If not, write to 00020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 * Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include "kshareddatacache.h" 00025 #include "kshareddatacache_p.h" // Various auxiliary support code 00026 00027 #include <kdebug.h> 00028 #include <kglobal.h> 00029 #include <kstandarddirs.h> 00030 #include <krandom.h> 00031 00032 #include <QtCore/QDateTime> 00033 #include <QtCore/QSharedPointer> 00034 #include <QtCore/QByteArray> 00035 #include <QtCore/QFile> 00036 #include <QtCore/QAtomicInt> 00037 #include <QtCore/QList> 00038 #include <QtCore/QMutex> 00039 #include <QtCore/QMutexLocker> 00040 00041 #include <sys/types.h> 00042 #include <sys/mman.h> 00043 #include <stdlib.h> 00044 00047 static const uint MAX_PROBE_COUNT = 6; 00048 00049 int ksdcArea() 00050 { 00051 static int s_ksdcArea = KDebug::registerArea("KSharedDataCache", false); 00052 return s_ksdcArea; 00053 } 00054 00063 class KSDCCorrupted 00064 { 00065 public: 00066 KSDCCorrupted() 00067 { 00068 kError(ksdcArea()) << "Error detected in cache, re-generating"; 00069 } 00070 }; 00071 00072 //----------------------------------------------------------------------------- 00073 // MurmurHashAligned, by Austin Appleby 00074 // (Released to the public domain, or licensed under the MIT license where 00075 // software may not be released to the public domain. See 00076 // http://sites.google.com/site/murmurhash/) 00077 00078 // Same algorithm as MurmurHash, but only does aligned reads - should be safer 00079 // on certain platforms. 00080 static unsigned int MurmurHashAligned(const void *key, int len, unsigned int seed) 00081 { 00082 const unsigned int m = 0xc6a4a793; 00083 const int r = 16; 00084 00085 const unsigned char * data = reinterpret_cast<const unsigned char *>(key); 00086 00087 unsigned int h = seed ^ (len * m); 00088 00089 int align = reinterpret_cast<quintptr>(data) & 3; 00090 00091 if(align & (len >= 4)) 00092 { 00093 // Pre-load the temp registers 00094 00095 unsigned int t = 0, d = 0; 00096 00097 switch(align) 00098 { 00099 case 1: t |= data[2] << 16; 00100 case 2: t |= data[1] << 8; 00101 case 3: t |= data[0]; 00102 } 00103 00104 t <<= (8 * align); 00105 00106 data += 4-align; 00107 len -= 4-align; 00108 00109 int sl = 8 * (4-align); 00110 int sr = 8 * align; 00111 00112 // Mix 00113 00114 while(len >= 4) 00115 { 00116 d = *reinterpret_cast<const unsigned int *>(data); 00117 t = (t >> sr) | (d << sl); 00118 h += t; 00119 h *= m; 00120 h ^= h >> r; 00121 t = d; 00122 00123 data += 4; 00124 len -= 4; 00125 } 00126 00127 // Handle leftover data in temp registers 00128 00129 int pack = len < align ? len : align; 00130 00131 d = 0; 00132 00133 switch(pack) 00134 { 00135 case 3: d |= data[2] << 16; 00136 case 2: d |= data[1] << 8; 00137 case 1: d |= data[0]; 00138 case 0: h += (t >> sr) | (d << sl); 00139 h *= m; 00140 h ^= h >> r; 00141 } 00142 00143 data += pack; 00144 len -= pack; 00145 } 00146 else 00147 { 00148 while(len >= 4) 00149 { 00150 h += *reinterpret_cast<const unsigned int *>(data); 00151 h *= m; 00152 h ^= h >> r; 00153 00154 data += 4; 00155 len -= 4; 00156 } 00157 } 00158 00159 //---------- 00160 // Handle tail bytes 00161 00162 switch(len) 00163 { 00164 case 3: h += data[2] << 16; 00165 case 2: h += data[1] << 8; 00166 case 1: h += data[0]; 00167 h *= m; 00168 h ^= h >> r; 00169 }; 00170 00171 h *= m; 00172 h ^= h >> 10; 00173 h *= m; 00174 h ^= h >> 17; 00175 00176 return h; 00177 } 00178 00183 static quint32 generateHash(const QByteArray &buffer) 00184 { 00185 // The final constant is the "seed" for MurmurHash. Do *not* change it 00186 // without incrementing the cache version. 00187 return MurmurHashAligned(buffer.data(), buffer.size(), 0xF0F00F0F); 00188 } 00189 00190 // Alignment concerns become a big deal when we're dealing with shared memory, 00191 // since trying to access a structure sized at, say 8 bytes at an address that 00192 // is not evenly divisible by 8 is a crash-inducing error on some 00193 // architectures. The compiler would normally take care of this, but with 00194 // shared memory the compiler will not necessarily know the alignment expected, 00195 // so make sure we account for this ourselves. To do so we need a way to find 00196 // out the expected alignment. Enter ALIGNOF... 00197 #ifndef ALIGNOF 00198 #if defined(Q_CC_GNU) || defined(Q_CC_SUN) 00199 #define ALIGNOF(x) (__alignof__ (x)) // GCC provides what we want directly 00200 #else 00201 00202 #include <stddef.h> // offsetof 00203 00204 template<class T> 00205 struct __alignmentHack 00206 { 00207 char firstEntry; 00208 T obj; 00209 static const size_t size = offsetof(__alignmentHack, obj); 00210 }; 00211 #define ALIGNOF(x) (__alignmentHack<x>::size) 00212 #endif // Non gcc 00213 #endif // ALIGNOF undefined 00214 00215 // Returns a pointer properly aligned to handle size alignment. 00216 // size should be a power of 2. start is assumed to be the lowest 00217 // permissible address, therefore the return value will be >= start. 00218 template<class T> 00219 T* alignTo(const void *start, uint size = ALIGNOF(T)) 00220 { 00221 quintptr mask = size - 1; 00222 00223 // Cast to int-type to handle bit-twiddling 00224 quintptr basePointer = reinterpret_cast<quintptr>(start); 00225 00226 // If (and only if) we are already aligned, adding mask into basePointer 00227 // will not increment any of the bits in ~mask and we get the right answer. 00228 basePointer = (basePointer + mask) & ~mask; 00229 00230 return reinterpret_cast<T *>(basePointer); 00231 } 00232 00239 template<class T> 00240 const T *offsetAs(const void *const base, qint32 offset) 00241 { 00242 const char *ptr = reinterpret_cast<const char*>(base); 00243 return alignTo<const T>(ptr + offset); 00244 } 00245 00246 // Same as above, but for non-const objects 00247 template<class T> 00248 T *offsetAs(void *const base, qint32 offset) 00249 { 00250 char *ptr = reinterpret_cast<char *>(base); 00251 return alignTo<T>(ptr + offset); 00252 } 00253 00259 unsigned intCeil(unsigned a, unsigned b) 00260 { 00261 if (KDE_ISUNLIKELY(b == 0)) { 00262 throw KSDCCorrupted(); 00263 } 00264 00265 return (a + b - 1) / b; 00266 } 00267 00271 static unsigned countSetBits(unsigned value) 00272 { 00273 // K&R / Wegner's algorithm used. GCC supports __builtin_popcount but we 00274 // expect there to always be only 1 bit set so this should be perhaps a bit 00275 // faster 99.9% of the time. 00276 unsigned count = 0; 00277 for (count = 0; value != 0; count++) { 00278 value &= (value - 1); // Clears least-significant set bit. 00279 } 00280 return count; 00281 } 00282 00283 typedef qint32 pageID; 00284 00285 // ========================================================================= 00286 // Description of the cache: 00287 // 00288 // The shared memory cache is designed to be handled as two separate objects, 00289 // all contained in the same global memory segment. First off, there is the 00290 // basic header data, consisting of the global header followed by the 00291 // accounting data necessary to hold items (described in more detail 00292 // momentarily). Following the accounting data is the start of the "page table" 00293 // (essentially just as you'd see it in an Operating Systems text). 00294 // 00295 // The page table contains shared memory split into fixed-size pages, with a 00296 // configurable page size. In the event that the data is too large to fit into 00297 // a single logical page, it will need to occupy consecutive pages of memory. 00298 // 00299 // The accounting data that was referenced earlier is split into two: 00300 // 00301 // 1. index table, containing a fixed-size list of possible cache entries. 00302 // Each index entry is of type IndexTableEntry (below), and holds the various 00303 // accounting data and a pointer to the first page. 00304 // 00305 // 2. page table, which is used to speed up the process of searching for 00306 // free pages of memory. There is one entry for every page in the page table, 00307 // and it contains the index of the one entry in the index table actually 00308 // holding the page (or <0 if the page is free). 00309 // 00310 // The entire segment looks like so: 00311 // ?════════?═════════════?════════════?═══════?═══════?═══════?═══════?═══? 00312 // ? Header │ Index Table │ Page Table ? Pages │ │ │ │...? 00313 // ?════════?═════════════?════════════?═══════?═══════?═══════?═══════?═══? 00314 // ========================================================================= 00315 00316 // All elements of this struct must be "plain old data" (POD) types since it 00317 // will be in shared memory. In addition, no pointers! To point to something 00318 // you must use relative offsets since the pointer start addresses will be 00319 // different in each process. 00320 struct IndexTableEntry 00321 { 00322 uint fileNameHash; 00323 uint totalItemSize; // in bytes 00324 mutable uint useCount; 00325 time_t addTime; 00326 mutable time_t lastUsedTime; 00327 pageID firstPage; 00328 }; 00329 00330 // Page table entry 00331 struct PageTableEntry 00332 { 00333 // int so we can use values <0 for unassigned pages. 00334 qint32 index; 00335 }; 00336 00337 // Each individual page contains the cached data. The first page starts off with 00338 // the utf8-encoded key, a null '\0', and then the data follows immediately 00339 // from the next byte, possibly crossing consecutive page boundaries to hold 00340 // all of the data. 00341 // There is, however, no specific struct for a page, it is simply a location in 00342 // memory. 00343 00344 // This is effectively the layout of the shared memory segment. The variables 00345 // contained within form the header, data contained afterwards is pointed to 00346 // by using special accessor functions. 00347 struct SharedMemory 00348 { 00356 enum { 00357 PIXMAP_CACHE_VERSION = 12, 00358 MINIMUM_CACHE_SIZE = 4096 00359 }; 00360 00361 // Note to those who follow me. You should not, under any circumstances, ever 00362 // re-arrange the following two fields, even if you change the version number 00363 // for later revisions of this code. 00364 QAtomicInt ready; 00365 quint8 version; 00366 00367 // See kshareddatacache_p.h 00368 SharedLock shmLock; 00369 00370 uint cacheSize; 00371 uint cacheAvail; 00372 QAtomicInt evictionPolicy; 00373 00374 // pageSize and cacheSize determine the number of pages. The number of 00375 // pages determine the page table size and (indirectly) the index table 00376 // size. 00377 QAtomicInt pageSize; 00378 00379 // This variable is added to reserve space for later cache timestamping 00380 // support. The idea is this variable will be updated when the cache is 00381 // written to, to allow clients to detect a changed cache quickly. 00382 QAtomicInt cacheTimestamp; 00383 00387 static unsigned equivalentPageSize(unsigned itemSize) 00388 { 00389 if (itemSize == 0) { 00390 return 4096; // Default average item size. 00391 } 00392 00393 int log2OfSize = 0; 00394 while ((itemSize >>= 1) != 0) { 00395 log2OfSize++; 00396 } 00397 00398 // Bound page size between 512 bytes and 256 KiB. 00399 // If this is adjusted, also alter validSizeMask in cachePageSize 00400 log2OfSize = qBound(9, log2OfSize, 18); 00401 00402 return (1 << log2OfSize); 00403 } 00404 00405 // Returns pageSize in unsigned format. 00406 unsigned cachePageSize() const 00407 { 00408 unsigned _pageSize = static_cast<unsigned>(pageSize); 00409 // bits 9-18 may be set. 00410 static const unsigned validSizeMask = 0x7FE00u; 00411 00412 // Check for page sizes that are not a power-of-2, or are too low/high. 00413 if (KDE_ISUNLIKELY(countSetBits(_pageSize) != 1 || (_pageSize & ~validSizeMask))) { 00414 throw KSDCCorrupted(); 00415 } 00416 00417 return _pageSize; 00418 } 00419 00432 bool performInitialSetup(uint _cacheSize, uint _pageSize) 00433 { 00434 if (_cacheSize < MINIMUM_CACHE_SIZE) { 00435 kError(ksdcArea()) << "Internal error: Attempted to create a cache sized < " 00436 << MINIMUM_CACHE_SIZE; 00437 return false; 00438 } 00439 00440 if (_pageSize == 0) { 00441 kError(ksdcArea()) << "Internal error: Attempted to create a cache with 0-sized pages."; 00442 return false; 00443 } 00444 00445 shmLock.type = findBestSharedLock(); 00446 if (shmLock.type == LOCKTYPE_INVALID) { 00447 kError(ksdcArea()) << "Unable to find an appropriate lock to guard the shared cache. " 00448 << "This *should* be essentially impossible. :("; 00449 return false; 00450 } 00451 00452 bool isProcessShared = false; 00453 QSharedPointer<KSDCLock> tempLock(createLockFromId(shmLock.type, shmLock)); 00454 00455 if (!tempLock->initialize(isProcessShared)) { 00456 kError(ksdcArea()) << "Unable to initialize the lock for the cache!"; 00457 return false; 00458 } 00459 00460 if (!isProcessShared) { 00461 kWarning(ksdcArea()) << "Cache initialized, but does not support being" 00462 << "shared across processes."; 00463 } 00464 00465 // These must be updated to make some of our auxiliary functions 00466 // work right since their values will be based on the cache size. 00467 cacheSize = _cacheSize; 00468 pageSize = _pageSize; 00469 version = PIXMAP_CACHE_VERSION; 00470 cacheTimestamp = static_cast<unsigned>(::time(0)); 00471 00472 clearInternalTables(); 00473 00474 // Unlock the mini-lock, and introduce a total memory barrier to make 00475 // sure all changes have propagated even without a mutex. 00476 ready.ref(); 00477 00478 return true; 00479 } 00480 00481 void clearInternalTables() 00482 { 00483 // Assumes we're already locked somehow. 00484 cacheAvail = pageTableSize(); 00485 00486 // Setup page tables to point nowhere 00487 PageTableEntry *table = pageTable(); 00488 for (uint i = 0; i < pageTableSize(); ++i) { 00489 table[i].index = -1; 00490 } 00491 00492 // Setup index tables to be accurate. 00493 IndexTableEntry *indices = indexTable(); 00494 for (uint i = 0; i < indexTableSize(); ++i) { 00495 indices[i].firstPage = -1; 00496 indices[i].useCount = 0; 00497 indices[i].fileNameHash = 0; 00498 indices[i].totalItemSize = 0; 00499 indices[i].addTime = 0; 00500 indices[i].lastUsedTime = 0; 00501 } 00502 } 00503 00504 const IndexTableEntry *indexTable() const 00505 { 00506 // Index Table goes immediately after this struct, at the first byte 00507 // where alignment constraints are met (accounted for by offsetAs). 00508 return offsetAs<IndexTableEntry>(this, sizeof(*this)); 00509 } 00510 00511 const PageTableEntry *pageTable() const 00512 { 00513 const IndexTableEntry *base = indexTable(); 00514 base += indexTableSize(); 00515 00516 // Let's call wherever we end up the start of the page table... 00517 return alignTo<PageTableEntry>(base); 00518 } 00519 00520 const void *cachePages() const 00521 { 00522 const PageTableEntry *tableStart = pageTable(); 00523 tableStart += pageTableSize(); 00524 00525 // Let's call wherever we end up the start of the data... 00526 return alignTo<void>(tableStart, cachePageSize()); 00527 } 00528 00529 const void *page(pageID at) const 00530 { 00531 if (static_cast<uint>(at) >= pageTableSize()) { 00532 return 0; 00533 } 00534 00535 // We must manually calculate this one since pageSize varies. 00536 const char *pageStart = reinterpret_cast<const char *>(cachePages()); 00537 pageStart += (at * cachePageSize()); 00538 00539 return reinterpret_cast<const void *>(pageStart); 00540 } 00541 00542 // The following are non-const versions of some of the methods defined 00543 // above. They use const_cast<> because I feel that is better than 00544 // duplicating the code. I suppose template member functions (?) 00545 // may work, may investigate later. 00546 IndexTableEntry *indexTable() 00547 { 00548 const SharedMemory *that = const_cast<const SharedMemory*>(this); 00549 return const_cast<IndexTableEntry *>(that->indexTable()); 00550 } 00551 00552 PageTableEntry *pageTable() 00553 { 00554 const SharedMemory *that = const_cast<const SharedMemory*>(this); 00555 return const_cast<PageTableEntry *>(that->pageTable()); 00556 } 00557 00558 void *cachePages() 00559 { 00560 const SharedMemory *that = const_cast<const SharedMemory*>(this); 00561 return const_cast<void *>(that->cachePages()); 00562 } 00563 00564 void *page(pageID at) 00565 { 00566 const SharedMemory *that = const_cast<const SharedMemory*>(this); 00567 return const_cast<void *>(that->page(at)); 00568 } 00569 00570 uint pageTableSize() const 00571 { 00572 return cacheSize / cachePageSize(); 00573 } 00574 00575 uint indexTableSize() const 00576 { 00577 // Assume 2 pages on average are needed -> the number of entries 00578 // would be half of the number of pages. 00579 return pageTableSize() / 2; 00580 } 00581 00586 pageID findEmptyPages(uint pagesNeeded) const 00587 { 00588 if (KDE_ISUNLIKELY(pagesNeeded > pageTableSize())) { 00589 return pageTableSize(); 00590 } 00591 00592 // Loop through the page table, find the first empty page, and just 00593 // makes sure that there are enough free pages. 00594 const PageTableEntry *table = pageTable(); 00595 uint contiguousPagesFound = 0; 00596 pageID base = 0; 00597 for (pageID i = 0; i < static_cast<int>(pageTableSize()); ++i) { 00598 if (table[i].index < 0) { 00599 if (contiguousPagesFound == 0) { 00600 base = i; 00601 } 00602 contiguousPagesFound++; 00603 } 00604 else { 00605 contiguousPagesFound = 0; 00606 } 00607 00608 if (contiguousPagesFound == pagesNeeded) { 00609 return base; 00610 } 00611 } 00612 00613 return pageTableSize(); 00614 } 00615 00616 // left < right? 00617 static bool lruCompare(const IndexTableEntry &l, const IndexTableEntry &r) 00618 { 00619 // Ensure invalid entries migrate to the end 00620 if (l.firstPage < 0 && r.firstPage >= 0) { 00621 return false; 00622 } 00623 if (l.firstPage >= 0 && r.firstPage < 0) { 00624 return true; 00625 } 00626 00627 // Most recently used will have the highest absolute time => 00628 // least recently used (lowest) should go first => use left < right 00629 return l.lastUsedTime < r.lastUsedTime; 00630 } 00631 00632 // left < right? 00633 static bool seldomUsedCompare(const IndexTableEntry &l, const IndexTableEntry &r) 00634 { 00635 // Ensure invalid entries migrate to the end 00636 if (l.firstPage < 0 && r.firstPage >= 0) { 00637 return false; 00638 } 00639 if (l.firstPage >= 0 && r.firstPage < 0) { 00640 return true; 00641 } 00642 00643 // Put lowest use count at start by using left < right 00644 return l.useCount < r.useCount; 00645 } 00646 00647 // left < right? 00648 static bool ageCompare(const IndexTableEntry &l, const IndexTableEntry &r) 00649 { 00650 // Ensure invalid entries migrate to the end 00651 if (l.firstPage < 0 && r.firstPage >= 0) { 00652 return false; 00653 } 00654 if (l.firstPage >= 0 && r.firstPage < 0) { 00655 return true; 00656 } 00657 00658 // Oldest entries die first -- they have the lowest absolute add time, 00659 // so just like the others use left < right 00660 return l.addTime < r.addTime; 00661 } 00662 00663 void defragment() 00664 { 00665 if (cacheAvail * cachePageSize() == cacheSize) { 00666 return; // That was easy 00667 } 00668 00669 kDebug(ksdcArea()) << "Defragmenting the shared cache"; 00670 00671 // Just do a linear scan, and anytime there is free space, swap it 00672 // with the pages to its right. In order to meet the precondition 00673 // we need to skip any used pages first. 00674 00675 pageID currentPage = 0; 00676 pageID idLimit = static_cast<pageID>(pageTableSize()); 00677 PageTableEntry *pages = pageTable(); 00678 00679 if (KDE_ISUNLIKELY(!pages || idLimit <= 0)) { 00680 throw KSDCCorrupted(); 00681 } 00682 00683 // Skip used pages 00684 while (currentPage < idLimit && pages[currentPage].index >= 0) { 00685 ++currentPage; 00686 } 00687 00688 pageID freeSpot = currentPage; 00689 00690 // Main loop, starting from a free page, skip to the used pages and 00691 // move them back. 00692 while (currentPage < idLimit) { 00693 // Find the next used page 00694 while (currentPage < idLimit && pages[currentPage].index < 0) { 00695 ++currentPage; 00696 } 00697 00698 if (currentPage >= idLimit) { 00699 break; 00700 } 00701 00702 // Found an entry, move it. 00703 qint32 affectedIndex = pages[currentPage].index; 00704 if (KDE_ISUNLIKELY(affectedIndex < 0 || 00705 affectedIndex >= idLimit || 00706 indexTable()[affectedIndex].firstPage != currentPage)) 00707 { 00708 throw KSDCCorrupted(); 00709 } 00710 00711 indexTable()[affectedIndex].firstPage = freeSpot; 00712 00713 // Moving one page at a time guarantees we can use memcpy safely 00714 // (in other words, the source and destination will not overlap). 00715 while (currentPage < idLimit && pages[currentPage].index >= 0) { 00716 const void *const sourcePage = page(currentPage); 00717 void *const destinationPage = page(freeSpot); 00718 00719 if(KDE_ISUNLIKELY(!sourcePage || !destinationPage)) { 00720 throw KSDCCorrupted(); 00721 } 00722 00723 ::memcpy(destinationPage, sourcePage, cachePageSize()); 00724 pages[freeSpot].index = affectedIndex; 00725 pages[currentPage].index = -1; 00726 ++currentPage; 00727 ++freeSpot; 00728 00729 // If we've just moved the very last page and it happened to 00730 // be at the very end of the cache then we're done. 00731 if (currentPage >= idLimit) { 00732 break; 00733 } 00734 00735 // We're moving consecutive used pages whether they belong to 00736 // our affected entry or not, so detect if we've started moving 00737 // the data for a different entry and adjust if necessary. 00738 if (affectedIndex != pages[currentPage].index) { 00739 indexTable()[pages[currentPage].index].firstPage = freeSpot; 00740 } 00741 affectedIndex = pages[currentPage].index; 00742 } 00743 00744 // At this point currentPage is on a page that is unused, and the 00745 // cycle repeats. However, currentPage is not the first unused 00746 // page, freeSpot is, so leave it alone. 00747 } 00748 } 00749 00756 qint32 findNamedEntry(const QByteArray &key) const 00757 { 00758 uint keyHash = generateHash(key); 00759 uint position = keyHash % indexTableSize(); 00760 uint probeNumber = 1; // See insert() for description 00761 00762 // Imagine 3 entries A, B, C in this logical probing chain. If B is 00763 // later removed then we can't find C either. So, we must keep 00764 // searching for probeNumber number of tries (or we find the item, 00765 // obviously). 00766 while (indexTable()[position].fileNameHash != keyHash && 00767 probeNumber < MAX_PROBE_COUNT) 00768 { 00769 position = (keyHash + (probeNumber + probeNumber * probeNumber) / 2) 00770 % indexTableSize(); 00771 probeNumber++; 00772 } 00773 00774 if (indexTable()[position].fileNameHash == keyHash) { 00775 pageID firstPage = indexTable()[position].firstPage; 00776 if (firstPage < 0 || static_cast<uint>(firstPage) >= pageTableSize()) { 00777 return -1; 00778 } 00779 00780 const void *resultPage = page(firstPage); 00781 if (KDE_ISUNLIKELY(!resultPage)) { 00782 throw KSDCCorrupted(); 00783 } 00784 00785 const char *utf8FileName = reinterpret_cast<const char *>(resultPage); 00786 if (qstrncmp(utf8FileName, key.constData(), cachePageSize()) == 0) { 00787 return position; 00788 } 00789 } 00790 00791 return -1; // Not found, or a different one found. 00792 } 00793 00794 // Function to use with QSharedPointer in removeUsedPages below... 00795 static void deleteTable(IndexTableEntry *table) { 00796 delete [] table; 00797 } 00798 00809 uint removeUsedPages(uint numberNeeded) 00810 { 00811 if (numberNeeded == 0) { 00812 kError(ksdcArea()) << "Internal error: Asked to remove exactly 0 pages for some reason."; 00813 throw KSDCCorrupted(); 00814 } 00815 00816 if (numberNeeded > pageTableSize()) { 00817 kError(ksdcArea()) << "Internal error: Requested more space than exists in the cache."; 00818 kError(ksdcArea()) << numberNeeded << "requested, " << pageTableSize() << "is the total possible."; 00819 throw KSDCCorrupted(); 00820 } 00821 00822 // If the cache free space is large enough we will defragment first 00823 // instead since it's likely we're highly fragmented. 00824 // Otherwise, we will (eventually) simply remove entries per the 00825 // eviction order set for the cache until there is enough room 00826 // available to hold the number of pages we need. 00827 00828 kDebug(ksdcArea()) << "Removing old entries to free up" << numberNeeded << "pages," 00829 << cacheAvail << "are already theoretically available."; 00830 00831 if (cacheAvail > 3 * numberNeeded) { 00832 defragment(); 00833 uint result = findEmptyPages(numberNeeded); 00834 00835 if (result < pageTableSize()) { 00836 return result; 00837 } 00838 else { 00839 kError(ksdcArea()) << "Just defragmented a locked cache, but still there" 00840 << "isn't enough room for the current request."; 00841 } 00842 } 00843 00844 // At this point we know we'll have to free some space up, so sort our 00845 // list of entries by whatever the current criteria are and start 00846 // killing expired entries. 00847 QSharedPointer<IndexTableEntry> tablePtr(new IndexTableEntry[indexTableSize()], deleteTable); 00848 00849 if (!tablePtr) { 00850 kError(ksdcArea()) << "Unable to allocate temporary memory for sorting the cache!"; 00851 clearInternalTables(); 00852 throw KSDCCorrupted(); 00853 } 00854 00855 // We use tablePtr to ensure the data is destroyed, but do the access 00856 // via a helper pointer to allow for array ops. 00857 IndexTableEntry *table = tablePtr.data(); 00858 00859 ::memcpy(table, indexTable(), sizeof(IndexTableEntry) * indexTableSize()); 00860 00861 // Our entry ID is simply its index into the 00862 // index table, which qSort will rearrange all willy-nilly, so first 00863 // we'll save the *real* entry ID into firstPage (which is useless in 00864 // our copy of the index table). On the other hand if the entry is not 00865 // used then we note that with -1. 00866 for (uint i = 0; i < indexTableSize(); ++i) { 00867 table[i].firstPage = table[i].useCount > 0 ? static_cast<pageID>(i) 00868 : -1; 00869 } 00870 00871 // Declare the comparison function that we'll use to pass to qSort, 00872 // based on our cache eviction policy. 00873 bool (*compareFunction)(const IndexTableEntry &, const IndexTableEntry &); 00874 switch((int) evictionPolicy) { 00875 case (int) KSharedDataCache::EvictLeastOftenUsed: 00876 case (int) KSharedDataCache::NoEvictionPreference: 00877 default: 00878 compareFunction = seldomUsedCompare; 00879 break; 00880 00881 case (int) KSharedDataCache::EvictLeastRecentlyUsed: 00882 compareFunction = lruCompare; 00883 break; 00884 00885 case (int) KSharedDataCache::EvictOldest: 00886 compareFunction = ageCompare; 00887 break; 00888 } 00889 00890 qSort(table, table + indexTableSize(), compareFunction); 00891 00892 // Least recently used entries will be in the front. 00893 // Start killing until we have room. 00894 00895 // Note on removeEntry: It expects an index into the index table, 00896 // but our sorted list is all jumbled. But we stored the real index 00897 // in the firstPage member. 00898 // Remove entries until we've removed at least the required number 00899 // of pages. 00900 uint i = 0; 00901 while (i < indexTableSize() && numberNeeded > cacheAvail) { 00902 int curIndex = table[i++].firstPage; // Really an index, not a page 00903 00904 // Removed everything, still no luck (or curIndex is set but too high). 00905 if (curIndex < 0 || static_cast<uint>(curIndex) >= indexTableSize()) { 00906 kError(ksdcArea()) << "Trying to remove index" << curIndex 00907 << "out-of-bounds for index table of size" << indexTableSize(); 00908 throw KSDCCorrupted(); 00909 } 00910 00911 kDebug(ksdcArea()) << "Removing entry of" << indexTable()[curIndex].totalItemSize 00912 << "size"; 00913 removeEntry(curIndex); 00914 } 00915 00916 // At this point let's see if we have freed up enough data by 00917 // defragmenting first and seeing if we can find that free space. 00918 defragment(); 00919 00920 pageID result = pageTableSize(); 00921 while (i < indexTableSize() && 00922 (static_cast<uint>(result = findEmptyPages(numberNeeded))) >= pageTableSize()) 00923 { 00924 int curIndex = table[i++].firstPage; 00925 00926 if (curIndex < 0) { 00927 // One last shot. 00928 defragment(); 00929 return findEmptyPages(numberNeeded); 00930 } 00931 00932 if (KDE_ISUNLIKELY(static_cast<uint>(curIndex) >= indexTableSize())) { 00933 throw KSDCCorrupted(); 00934 } 00935 00936 removeEntry(curIndex); 00937 } 00938 00939 // Whew. 00940 return result; 00941 } 00942 00943 // Returns the total size required for a given cache size. 00944 static uint totalSize(uint cacheSize, uint effectivePageSize) 00945 { 00946 uint numberPages = intCeil(cacheSize, effectivePageSize); 00947 uint indexTableSize = numberPages / 2; 00948 00949 // Knowing the number of pages, we can determine what addresses we'd be 00950 // using (properly aligned), and from there determine how much memory 00951 // we'd use. 00952 IndexTableEntry *indexTableStart = 00953 offsetAs<IndexTableEntry>(static_cast<void*>(0), sizeof (SharedMemory)); 00954 00955 indexTableStart += indexTableSize; 00956 00957 PageTableEntry *pageTableStart = reinterpret_cast<PageTableEntry *>(indexTableStart); 00958 pageTableStart = alignTo<PageTableEntry>(pageTableStart); 00959 pageTableStart += numberPages; 00960 00961 // The weird part, we must manually adjust the pointer based on the page size. 00962 char *cacheStart = reinterpret_cast<char *>(pageTableStart); 00963 cacheStart += (numberPages * effectivePageSize); 00964 00965 // ALIGNOF gives pointer alignment 00966 cacheStart = alignTo<char>(cacheStart, ALIGNOF(void*)); 00967 00968 // We've traversed the header, index, page table, and cache. 00969 // Wherever we're at now is the size of the enchilada. 00970 return static_cast<uint>(reinterpret_cast<quintptr>(cacheStart)); 00971 } 00972 00973 uint fileNameHash(const QByteArray &utf8FileName) const 00974 { 00975 return generateHash(utf8FileName) % indexTableSize(); 00976 } 00977 00978 void clear() 00979 { 00980 clearInternalTables(); 00981 } 00982 00983 void removeEntry(uint index); 00984 }; 00985 00986 // The per-instance private data, such as map size, whether 00987 // attached or not, pointer to shared memory, etc. 00988 class KSharedDataCache::Private 00989 { 00990 public: 00991 Private(const QString &name, 00992 unsigned defaultCacheSize, 00993 unsigned expectedItemSize 00994 ) 00995 : m_cacheName(name) 00996 , shm(0) 00997 , m_lock(0) 00998 , m_mapSize(0) 00999 , m_defaultCacheSize(defaultCacheSize) 01000 , m_expectedItemSize(expectedItemSize) 01001 , m_expectedType(LOCKTYPE_INVALID) 01002 { 01003 mapSharedMemory(); 01004 } 01005 01006 // Put the cache in a condition to be able to call mapSharedMemory() by 01007 // completely detaching from shared memory (such as to respond to an 01008 // unrecoverable error). 01009 // m_mapSize must already be set to the amount of memory mapped to shm. 01010 void detachFromSharedMemory() 01011 { 01012 // The lock holds a reference into shared memory, so this must be 01013 // cleared before shm is removed. 01014 m_lock.clear(); 01015 01016 if (shm && !::munmap(shm, m_mapSize)) { 01017 kError(ksdcArea()) << "Unable to unmap shared memory segment" 01018 << static_cast<void*>(shm); 01019 } 01020 01021 shm = 0; 01022 m_mapSize = 0; 01023 } 01024 01025 // This function does a lot of the important work, attempting to connect to shared 01026 // memory, a private anonymous mapping if that fails, and failing that, nothing (but 01027 // the cache remains "valid", we just don't actually do anything). 01028 void mapSharedMemory() 01029 { 01030 // 0-sized caches are fairly useless. 01031 unsigned cacheSize = qMax(m_defaultCacheSize, uint(SharedMemory::MINIMUM_CACHE_SIZE)); 01032 unsigned pageSize = SharedMemory::equivalentPageSize(m_expectedItemSize); 01033 01034 // Ensure that the cache is sized such that there is a minimum number of 01035 // pages available. (i.e. a cache consisting of only 1 page is fairly 01036 // useless and probably crash-prone). 01037 cacheSize = qMax(pageSize * 256, cacheSize); 01038 01039 // The m_cacheName is used to find the file to store the cache in. 01040 QString cacheName = KGlobal::dirs()->locateLocal("cache", m_cacheName + QLatin1String(".kcache")); 01041 QFile file(cacheName); 01042 01043 // The basic idea is to open the file that we want to map into shared 01044 // memory, and then actually establish the mapping. Once we have mapped the 01045 // file into shared memory we can close the file handle, the mapping will 01046 // still be maintained (unless the file is resized to be shorter than 01047 // expected, which we don't handle yet :-( ) 01048 01049 // size accounts for the overhead over the desired cacheSize 01050 uint size = SharedMemory::totalSize(cacheSize, pageSize); 01051 void *mapAddress = MAP_FAILED; 01052 01053 if (size < cacheSize) { 01054 kError(ksdcArea()) << "Asked for a cache size less than requested size somehow -- Logic Error :("; 01055 return; 01056 } 01057 01058 // We establish the shared memory mapping here, only if we will have appropriate 01059 // mutex support (systemSupportsProcessSharing), then we: 01060 // Open the file and resize to some sane value if the file is too small. 01061 if (file.open(QIODevice::ReadWrite) && 01062 (file.size() >= size || file.resize(size)) && 01063 ensureFileAllocated(file.handle(), size)) 01064 { 01065 // Use mmap directly instead of QFile::map since the QFile (and its 01066 // shared mapping) will disappear unless we hang onto the QFile for no 01067 // reason (see the note below, we don't care about the file per se...) 01068 mapAddress = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file.handle(), 0); 01069 01070 // So... it is possible that someone else has mapped this cache already 01071 // with a larger size. If that's the case we need to at least match 01072 // the size to be able to access every entry, so fixup the mapping. 01073 if (mapAddress != MAP_FAILED) { 01074 SharedMemory *mapped = reinterpret_cast<SharedMemory *>(mapAddress); 01075 01076 // First make sure that the version of the cache on disk is 01077 // valid. We also need to check that version != 0 to 01078 // disambiguate against an uninitialized cache. 01079 if (mapped->version != SharedMemory::PIXMAP_CACHE_VERSION && 01080 mapped->version > 0) 01081 { 01082 kWarning(ksdcArea()) << "Deleting wrong version of cache" << cacheName; 01083 01084 // CAUTION: Potentially recursive since the recovery 01085 // involves calling this function again. 01086 m_mapSize = size; 01087 shm = mapped; 01088 recoverCorruptedCache(); 01089 return; 01090 } 01091 else if (mapped->cacheSize > cacheSize) { 01092 // This order is very important. We must save the cache size 01093 // before we remove the mapping, but unmap before overwriting 01094 // the previous mapping size... 01095 cacheSize = mapped->cacheSize; 01096 unsigned actualPageSize = mapped->cachePageSize(); 01097 ::munmap(mapAddress, size); 01098 size = SharedMemory::totalSize(cacheSize, actualPageSize); 01099 mapAddress = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file.handle(), 0); 01100 } 01101 } 01102 } 01103 01104 // We could be here without the mapping established if: 01105 // 1) Process-shared synchronization is not supported, either at compile or run time, 01106 // 2) Unable to open the required file. 01107 // 3) Unable to resize the file to be large enough. 01108 // 4) Establishing the mapping failed. 01109 // 5) The mapping succeeded, but the size was wrong and we were unable to map when 01110 // we tried again. 01111 // 6) The incorrect version of the cache was detected. 01112 // 7) The file could be created, but posix_fallocate failed to commit it fully to disk. 01113 // In any of these cases, attempt to fallback to the 01114 // better-supported anonymous private page style of mmap. This memory won't 01115 // be shared, but our code will still work the same. 01116 // NOTE: We never use the on-disk representation independently of the 01117 // shared memory. If we don't get shared memory the disk info is ignored, 01118 // if we do get shared memory we never look at disk again. 01119 if (mapAddress == MAP_FAILED) { 01120 kWarning(ksdcArea()) << "Failed to establish shared memory mapping, will fallback" 01121 << "to private memory -- memory usage will increase"; 01122 01123 mapAddress = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 01124 } 01125 01126 // Well now we're really hosed. We can still work, but we can't even cache 01127 // data. 01128 if (mapAddress == MAP_FAILED) { 01129 kError(ksdcArea()) << "Unable to allocate shared memory segment for shared data cache" 01130 << cacheName << "of size" << cacheSize; 01131 return; 01132 } 01133 01134 m_mapSize = size; 01135 01136 // We never actually construct shm, but we assign it the same address as the 01137 // shared memory we just mapped, so effectively shm is now a SharedMemory that 01138 // happens to be located at mapAddress. 01139 shm = reinterpret_cast<SharedMemory *>(mapAddress); 01140 01141 // If we were first to create this memory map, all data will be 0. 01142 // Therefore if ready == 0 we're not initialized. A fully initialized 01143 // header will have ready == 2. Why? 01144 // Because 0 means "safe to initialize" 01145 // 1 means "in progress of initing" 01146 // 2 means "ready" 01147 uint usecSleepTime = 8; // Start by sleeping for 8 microseconds 01148 while (shm->ready != 2) { 01149 if (KDE_ISUNLIKELY(usecSleepTime >= (1 << 21))) { 01150 // Didn't acquire within ~8 seconds? Assume an issue exists 01151 kError(ksdcArea()) << "Unable to acquire shared lock, is the cache corrupt?"; 01152 01153 file.remove(); // Unlink the cache in case it's corrupt. 01154 detachFromSharedMemory(); 01155 return; // Fallback to QCache (later) 01156 } 01157 01158 if (shm->ready.testAndSetAcquire(0, 1)) { 01159 if (!shm->performInitialSetup(cacheSize, pageSize)) { 01160 kError(ksdcArea()) << "Unable to perform initial setup, this system probably " 01161 "does not really support process-shared pthreads or " 01162 "semaphores, even though it claims otherwise."; 01163 01164 file.remove(); 01165 detachFromSharedMemory(); 01166 return; 01167 } 01168 } 01169 else { 01170 usleep(usecSleepTime); // spin 01171 01172 // Exponential fallback as in Ethernet and similar collision resolution methods 01173 usecSleepTime *= 2; 01174 } 01175 } 01176 01177 m_expectedType = shm->shmLock.type; 01178 m_lock = QSharedPointer<KSDCLock>(createLockFromId(m_expectedType, shm->shmLock)); 01179 bool isProcessSharingSupported = false; 01180 01181 if (!m_lock->initialize(isProcessSharingSupported)) { 01182 kError(ksdcArea()) << "Unable to setup shared cache lock, although it worked when created."; 01183 detachFromSharedMemory(); 01184 } 01185 } 01186 01187 // Called whenever the cache is apparently corrupt (for instance, a timeout trying to 01188 // lock the cache). In this situation it is safer just to destroy it all and try again. 01189 void recoverCorruptedCache() 01190 { 01191 KSharedDataCache::deleteCache(m_cacheName); 01192 01193 detachFromSharedMemory(); 01194 01195 // Do this even if we weren't previously cached -- it might work now. 01196 mapSharedMemory(); 01197 } 01198 01199 // This should be called for any memory access to shared memory. This 01200 // function will verify that the bytes [base, base+accessLength) are 01201 // actually mapped to d->shm. The cache itself may have incorrect cache 01202 // page sizes, incorrect cache size, etc. so this function should be called 01203 // despite the cache data indicating it should be safe. 01204 // 01205 // If the access is /not/ safe then a KSDCCorrupted exception will be 01206 // thrown, so be ready to catch that. 01207 void verifyProposedMemoryAccess(const void *base, unsigned accessLength) const 01208 { 01209 quintptr startOfAccess = reinterpret_cast<quintptr>(base); 01210 quintptr startOfShm = reinterpret_cast<quintptr>(shm); 01211 01212 if (KDE_ISUNLIKELY(startOfAccess < startOfShm)) { 01213 throw KSDCCorrupted(); 01214 } 01215 01216 quintptr endOfShm = startOfShm + m_mapSize; 01217 quintptr endOfAccess = startOfAccess + accessLength; 01218 01219 // Check for unsigned integer wraparound, and then 01220 // bounds access 01221 if (KDE_ISUNLIKELY((endOfShm < startOfShm) || 01222 (endOfAccess < startOfAccess) || 01223 (endOfAccess > endOfShm))) 01224 { 01225 throw KSDCCorrupted(); 01226 } 01227 } 01228 01229 bool lock() const 01230 { 01231 if (KDE_ISLIKELY(shm && shm->shmLock.type == m_expectedType)) { 01232 return m_lock->lock(); 01233 } 01234 01235 // No shm or wrong type --> corrupt! 01236 throw KSDCCorrupted(); 01237 } 01238 01239 void unlock() const 01240 { 01241 m_lock->unlock(); 01242 } 01243 01244 class CacheLocker 01245 { 01246 mutable Private * d; 01247 01248 bool cautiousLock() 01249 { 01250 int lockCount = 0; 01251 01252 // Locking can fail due to a timeout. If it happens too often even though 01253 // we're taking corrective action assume there's some disastrous problem 01254 // and give up. 01255 while (!d->lock()) { 01256 d->recoverCorruptedCache(); 01257 01258 if (!d->shm) { 01259 kWarning(ksdcArea()) << "Lost the connection to shared memory for cache" 01260 << d->m_cacheName; 01261 return false; 01262 } 01263 01264 if (lockCount++ > 4) { 01265 kError(ksdcArea()) << "There is a very serious problem with the KDE data cache" 01266 << d->m_cacheName << "giving up trying to access cache."; 01267 d->detachFromSharedMemory(); 01268 return false; 01269 } 01270 } 01271 01272 return true; 01273 } 01274 01275 public: 01276 CacheLocker(const Private *_d) : d(const_cast<Private *>(_d)) 01277 { 01278 if (KDE_ISUNLIKELY(!d || !d->shm || !cautiousLock())) { 01279 return; 01280 } 01281 01282 uint testSize = SharedMemory::totalSize(d->shm->cacheSize, d->shm->cachePageSize()); 01283 01284 // A while loop? Indeed, think what happens if this happens 01285 // twice -- hard to debug race conditions. 01286 while (testSize > d->m_mapSize) { 01287 kDebug(ksdcArea()) << "Someone enlarged the cache on us," 01288 << "attempting to match new configuration."; 01289 01290 // Protect against two threads accessing this same KSDC 01291 // from trying to execute the following remapping at the 01292 // same time. 01293 QMutexLocker d_locker(&d->m_threadLock); 01294 if (testSize == d->m_mapSize) { 01295 break; // Bail if the other thread already solved. 01296 } 01297 01298 // Linux supports mremap, but it's not portable. So, 01299 // drop the map and (try to) re-establish. 01300 d->unlock(); 01301 01302 #ifdef KSDC_MSYNC_SUPPORTED 01303 ::msync(d->shm, d->m_mapSize, MS_INVALIDATE | MS_ASYNC); 01304 #endif 01305 ::munmap(d->shm, d->m_mapSize); 01306 d->m_mapSize = 0; 01307 d->shm = 0; 01308 01309 QFile f(d->m_cacheName); 01310 if (!f.open(QFile::ReadWrite)) { 01311 kError(ksdcArea()) << "Unable to re-open cache, unfortunately" 01312 << "the connection had to be dropped for" 01313 << "crash safety -- things will be much" 01314 << "slower now."; 01315 return; 01316 } 01317 01318 void *newMap = ::mmap(0, testSize, PROT_READ | PROT_WRITE, 01319 MAP_SHARED, f.handle(), 0); 01320 if (newMap == MAP_FAILED) { 01321 kError(ksdcArea()) << "Unopen to re-map the cache into memory" 01322 << "things will be much slower now"; 01323 return; 01324 } 01325 01326 d->shm = reinterpret_cast<SharedMemory *>(newMap); 01327 d->m_mapSize = testSize; 01328 01329 if (!cautiousLock()) { 01330 return; 01331 } 01332 01333 testSize = SharedMemory::totalSize(d->shm->cacheSize, d->shm->cachePageSize()); 01334 } 01335 } 01336 01337 ~CacheLocker() 01338 { 01339 if (d && d->shm) { 01340 d->unlock(); 01341 } 01342 } 01343 01344 bool failed() const 01345 { 01346 return !d || d->shm == 0; 01347 } 01348 }; 01349 01350 QString m_cacheName; 01351 QMutex m_threadLock; 01352 SharedMemory *shm; 01353 QSharedPointer<KSDCLock> m_lock; 01354 uint m_mapSize; 01355 uint m_defaultCacheSize; 01356 uint m_expectedItemSize; 01357 SharedLockId m_expectedType; 01358 }; 01359 01360 // Must be called while the lock is already held! 01361 void SharedMemory::removeEntry(uint index) 01362 { 01363 if (index >= indexTableSize() || cacheAvail > pageTableSize()) { 01364 throw KSDCCorrupted(); 01365 } 01366 01367 PageTableEntry *pageTableEntries = pageTable(); 01368 IndexTableEntry *entriesIndex = indexTable(); 01369 01370 // Update page table first 01371 pageID firstPage = entriesIndex[index].firstPage; 01372 if (firstPage < 0 || static_cast<quint32>(firstPage) >= pageTableSize()) { 01373 kDebug(ksdcArea()) << "Trying to remove an entry which is already invalid. This " 01374 << "cache is likely corrupt."; 01375 throw KSDCCorrupted(); 01376 } 01377 01378 if (index != static_cast<uint>(pageTableEntries[firstPage].index)) { 01379 kError(ksdcArea()) << "Removing entry" << index << "but the matching data" 01380 << "doesn't link back -- cache is corrupt, clearing."; 01381 throw KSDCCorrupted(); 01382 } 01383 01384 uint entriesToRemove = intCeil(entriesIndex[index].totalItemSize, cachePageSize()); 01385 uint savedCacheSize = cacheAvail; 01386 for (uint i = firstPage; i < pageTableSize() && 01387 (uint) pageTableEntries[i].index == index; ++i) 01388 { 01389 pageTableEntries[i].index = -1; 01390 cacheAvail++; 01391 } 01392 01393 if ((cacheAvail - savedCacheSize) != entriesToRemove) { 01394 kError(ksdcArea()) << "We somehow did not remove" << entriesToRemove 01395 << "when removing entry" << index << ", instead we removed" 01396 << (cacheAvail - savedCacheSize); 01397 throw KSDCCorrupted(); 01398 } 01399 01400 // For debugging 01401 #ifdef NDEBUG 01402 void *const startOfData = page(firstPage); 01403 if (startOfData) { 01404 QByteArray str((const char *) startOfData); 01405 str.prepend(" REMOVED: "); 01406 str.prepend(QByteArray::number(index)); 01407 str.prepend("ENTRY "); 01408 01409 ::memcpy(startOfData, str.constData(), str.size() + 1); 01410 } 01411 #endif 01412 01413 // Update the index 01414 entriesIndex[index].fileNameHash = 0; 01415 entriesIndex[index].totalItemSize = 0; 01416 entriesIndex[index].useCount = 0; 01417 entriesIndex[index].lastUsedTime = 0; 01418 entriesIndex[index].addTime = 0; 01419 entriesIndex[index].firstPage = -1; 01420 } 01421 01422 KSharedDataCache::KSharedDataCache(const QString &cacheName, 01423 unsigned defaultCacheSize, 01424 unsigned expectedItemSize) 01425 : d(0) 01426 { 01427 try { 01428 d = new Private(cacheName, defaultCacheSize, expectedItemSize); 01429 } 01430 catch(KSDCCorrupted) { 01431 KSharedDataCache::deleteCache(cacheName); 01432 01433 // Try only once more 01434 try { 01435 d = new Private(cacheName, defaultCacheSize, expectedItemSize); 01436 } 01437 catch(KSDCCorrupted) { 01438 kError(ksdcArea()) 01439 << "Even a brand-new cache starts off corrupted, something is" 01440 << "seriously wrong. :-("; 01441 d = 0; // Just in case 01442 } 01443 } 01444 } 01445 01446 KSharedDataCache::~KSharedDataCache() 01447 { 01448 // Note that there is no other actions required to separate from the 01449 // shared memory segment, simply unmapping is enough. This makes things 01450 // *much* easier so I'd recommend maintaining this ideal. 01451 if (!d) { 01452 return; 01453 } 01454 01455 if (d->shm) { 01456 #ifdef KSDC_MSYNC_SUPPORTED 01457 ::msync(d->shm, d->m_mapSize, MS_INVALIDATE | MS_ASYNC); 01458 #endif 01459 ::munmap(d->shm, d->m_mapSize); 01460 } 01461 01462 // Do not delete d->shm, it was never constructed, it's just an alias. 01463 d->shm = 0; 01464 01465 delete d; 01466 } 01467 01468 bool KSharedDataCache::insert(const QString &key, const QByteArray &data) 01469 { 01470 try { 01471 Private::CacheLocker lock(d); 01472 if (lock.failed()) { 01473 return false; 01474 } 01475 01476 QByteArray encodedKey = key.toUtf8(); 01477 uint keyHash = generateHash(encodedKey); 01478 uint position = keyHash % d->shm->indexTableSize(); 01479 01480 // See if we're overwriting an existing entry. 01481 IndexTableEntry *indices = d->shm->indexTable(); 01482 01483 // In order to avoid the issue of a very long-lived cache having items 01484 // with a use count of 1 near-permanently, we attempt to artifically 01485 // reduce the use count of long-lived items when there is high load on 01486 // the cache. We do this randomly, with a weighting that makes the event 01487 // impossible if load < 0.5, and guaranteed if load >= 0.96. 01488 const static double startCullPoint = 0.5l; 01489 const static double mustCullPoint = 0.96l; 01490 01491 // cacheAvail is in pages, cacheSize is in bytes. 01492 double loadFactor = 1.0 - (1.0l * d->shm->cacheAvail * d->shm->cachePageSize() 01493 / d->shm->cacheSize); 01494 bool cullCollisions = false; 01495 01496 if (KDE_ISUNLIKELY(loadFactor >= mustCullPoint)) { 01497 cullCollisions = true; 01498 } 01499 else if (loadFactor > startCullPoint) { 01500 const int tripWireValue = RAND_MAX * (loadFactor - startCullPoint) / (mustCullPoint - startCullPoint); 01501 if (KRandom::random() >= tripWireValue) { 01502 cullCollisions = true; 01503 } 01504 } 01505 01506 // In case of collisions in the index table (i.e. identical positions), use 01507 // quadratic chaining to attempt to find an empty slot. The equation we use 01508 // is: 01509 // position = (hash + (i + i*i) / 2) % size, where i is the probe number. 01510 uint probeNumber = 1; 01511 while (indices[position].useCount > 0 && probeNumber < MAX_PROBE_COUNT) { 01512 // If we actually stumbled upon an old version of the key we are 01513 // overwriting, then use that position, do not skip over it. 01514 01515 if (KDE_ISUNLIKELY(indices[position].fileNameHash == keyHash)) { 01516 break; 01517 } 01518 01519 // If we are "culling" old entries, see if this one is old and if so 01520 // reduce its use count. If it reduces to zero then eliminate it and 01521 // use its old spot. 01522 01523 if (cullCollisions && (::time(0) - indices[position].lastUsedTime) > 60) { 01524 indices[position].useCount >>= 1; 01525 if (indices[position].useCount == 0) { 01526 kDebug(ksdcArea()) << "Overwriting existing old cached entry due to collision."; 01527 d->shm->removeEntry(position); // Remove it first 01528 01529 break; 01530 } 01531 } 01532 01533 position = (keyHash + (probeNumber + probeNumber * probeNumber) / 2) 01534 % d->shm->indexTableSize(); 01535 probeNumber++; 01536 } 01537 01538 if (indices[position].useCount > 0 && indices[position].firstPage >= 0) { 01539 kDebug(ksdcArea()) << "Overwriting existing cached entry due to collision."; 01540 d->shm->removeEntry(position); // Remove it first 01541 } 01542 01543 // Data will be stored as fileNamefoo\0PNGimagedata..... 01544 // So total size required is the length of the encoded file name + 1 01545 // for the trailing null, and then the length of the image data. 01546 uint fileNameLength = 1 + encodedKey.length(); 01547 uint requiredSize = fileNameLength + data.size(); 01548 uint pagesNeeded = intCeil(requiredSize, d->shm->cachePageSize()); 01549 uint firstPage = (uint) -1; 01550 01551 if (pagesNeeded >= d->shm->pageTableSize()) { 01552 kWarning(ksdcArea()) << key << "is too large to be cached."; 01553 return false; 01554 } 01555 01556 // If the cache has no room, or the fragmentation is too great to find 01557 // the required number of consecutive free pages, take action. 01558 if (pagesNeeded > d->shm->cacheAvail || 01559 (firstPage = d->shm->findEmptyPages(pagesNeeded)) >= d->shm->pageTableSize()) 01560 { 01561 // If we have enough free space just defragment 01562 uint freePagesDesired = 3 * qMax(1u, pagesNeeded / 2); 01563 01564 if (d->shm->cacheAvail > freePagesDesired) { 01565 // TODO: How the hell long does this actually take on real 01566 // caches? 01567 d->shm->defragment(); 01568 firstPage = d->shm->findEmptyPages(pagesNeeded); 01569 } 01570 else { 01571 // If we already have free pages we don't want to remove a ton 01572 // extra. However we can't rely on the return value of 01573 // removeUsedPages giving us a good location since we're not 01574 // passing in the actual number of pages that we need. 01575 d->shm->removeUsedPages(qMin(2 * freePagesDesired, d->shm->pageTableSize()) 01576 - d->shm->cacheAvail); 01577 firstPage = d->shm->findEmptyPages(pagesNeeded); 01578 } 01579 01580 if (firstPage >= d->shm->pageTableSize() || 01581 d->shm->cacheAvail < pagesNeeded) 01582 { 01583 kError(ksdcArea()) << "Unable to free up memory for" << key; 01584 return false; 01585 } 01586 } 01587 01588 // Update page table 01589 PageTableEntry *table = d->shm->pageTable(); 01590 for (uint i = 0; i < pagesNeeded; ++i) { 01591 table[firstPage + i].index = position; 01592 } 01593 01594 // Update index 01595 indices[position].fileNameHash = keyHash; 01596 indices[position].totalItemSize = requiredSize; 01597 indices[position].useCount = 1; 01598 indices[position].addTime = ::time(0); 01599 indices[position].lastUsedTime = indices[position].addTime; 01600 indices[position].firstPage = firstPage; 01601 01602 // Update cache 01603 d->shm->cacheAvail -= pagesNeeded; 01604 01605 // Actually move the data in place 01606 void *dataPage = d->shm->page(firstPage); 01607 if (KDE_ISUNLIKELY(!dataPage)) { 01608 throw KSDCCorrupted(); 01609 } 01610 01611 // Verify it will all fit 01612 d->verifyProposedMemoryAccess(dataPage, requiredSize); 01613 01614 // Cast for byte-sized pointer arithmetic 01615 uchar *startOfPageData = reinterpret_cast<uchar *>(dataPage); 01616 ::memcpy(startOfPageData, encodedKey.constData(), fileNameLength); 01617 ::memcpy(startOfPageData + fileNameLength, data.constData(), data.size()); 01618 01619 return true; 01620 } 01621 catch(KSDCCorrupted) { 01622 d->recoverCorruptedCache(); 01623 return false; 01624 } 01625 } 01626 01627 bool KSharedDataCache::find(const QString &key, QByteArray *destination) const 01628 { 01629 try { 01630 Private::CacheLocker lock(d); 01631 if (lock.failed()) { 01632 return false; 01633 } 01634 01635 // Search in the index for our data, hashed by key; 01636 QByteArray encodedKey = key.toUtf8(); 01637 qint32 entry = d->shm->findNamedEntry(encodedKey); 01638 01639 if (entry >= 0) { 01640 const IndexTableEntry *header = &d->shm->indexTable()[entry]; 01641 const void *resultPage = d->shm->page(header->firstPage); 01642 if (KDE_ISUNLIKELY(!resultPage)) { 01643 throw KSDCCorrupted(); 01644 } 01645 01646 d->verifyProposedMemoryAccess(resultPage, header->totalItemSize); 01647 01648 header->useCount++; 01649 header->lastUsedTime = ::time(0); 01650 01651 // Our item is the key followed immediately by the data, so skip 01652 // past the key. 01653 const char *cacheData = reinterpret_cast<const char *>(resultPage); 01654 cacheData += encodedKey.size(); 01655 cacheData++; // Skip trailing null -- now we're pointing to start of data 01656 01657 if (destination) { 01658 *destination = QByteArray(cacheData, header->totalItemSize - encodedKey.size() - 1); 01659 } 01660 01661 return true; 01662 } 01663 } 01664 catch(KSDCCorrupted) { 01665 d->recoverCorruptedCache(); 01666 } 01667 01668 return false; 01669 } 01670 01671 void KSharedDataCache::clear() 01672 { 01673 try { 01674 Private::CacheLocker lock(d); 01675 01676 if(!lock.failed()) { 01677 d->shm->clear(); 01678 } 01679 } 01680 catch(KSDCCorrupted) { 01681 d->recoverCorruptedCache(); 01682 } 01683 } 01684 01685 bool KSharedDataCache::contains(const QString &key) const 01686 { 01687 try { 01688 Private::CacheLocker lock(d); 01689 if (lock.failed()) { 01690 return false; 01691 } 01692 01693 return d->shm->findNamedEntry(key.toUtf8()) >= 0; 01694 } 01695 catch(KSDCCorrupted) { 01696 d->recoverCorruptedCache(); 01697 return false; 01698 } 01699 } 01700 01701 void KSharedDataCache::deleteCache(const QString &cacheName) 01702 { 01703 QString cachePath = KGlobal::dirs()->locateLocal("cache", cacheName + QLatin1String(".kcache")); 01704 01705 // Note that it is important to simply unlink the file, and not truncate it 01706 // smaller first to avoid SIGBUS errors and similar with shared memory 01707 // attached to the underlying inode. 01708 kDebug(ksdcArea()) << "Removing cache at" << cachePath; 01709 QFile::remove(cachePath); 01710 } 01711 01712 unsigned KSharedDataCache::totalSize() const 01713 { 01714 try { 01715 Private::CacheLocker lock(d); 01716 if (lock.failed()) { 01717 return 0u; 01718 } 01719 01720 return d->shm->cacheSize; 01721 } 01722 catch(KSDCCorrupted) { 01723 d->recoverCorruptedCache(); 01724 return 0u; 01725 } 01726 } 01727 01728 unsigned KSharedDataCache::freeSize() const 01729 { 01730 try { 01731 Private::CacheLocker lock(d); 01732 if (lock.failed()) { 01733 return 0u; 01734 } 01735 01736 return d->shm->cacheAvail * d->shm->cachePageSize(); 01737 } 01738 catch(KSDCCorrupted) { 01739 d->recoverCorruptedCache(); 01740 return 0u; 01741 } 01742 } 01743 01744 KSharedDataCache::EvictionPolicy KSharedDataCache::evictionPolicy() const 01745 { 01746 if (d && d->shm) { 01747 return static_cast<EvictionPolicy>(d->shm->evictionPolicy.fetchAndAddAcquire(0)); 01748 } 01749 01750 return NoEvictionPreference; 01751 } 01752 01753 void KSharedDataCache::setEvictionPolicy(EvictionPolicy newPolicy) 01754 { 01755 if (d && d->shm) { 01756 d->shm->evictionPolicy.fetchAndStoreRelease(static_cast<int>(newPolicy)); 01757 } 01758 } 01759 01760 unsigned KSharedDataCache::timestamp() const 01761 { 01762 if (d && d->shm) { 01763 return static_cast<unsigned>(d->shm->cacheTimestamp.fetchAndAddAcquire(0)); 01764 } 01765 01766 return 0; 01767 } 01768 01769 void KSharedDataCache::setTimestamp(unsigned newTimestamp) 01770 { 01771 if (d && d->shm) { 01772 d->shm->cacheTimestamp.fetchAndStoreRelease(static_cast<int>(newTimestamp)); 01773 } 01774 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:12 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:12 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.