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