bool LargeObjectCache::CacheBin::put(ExtMemoryPool *extMemPool, LargeMemoryBlock *ptr) { bool blockCached = true; ptr->prev = NULL; ptr->age = extMemPool->loc.cleanupCacheIfNeed(extMemPool); { MallocMutex::scoped_lock scoped_cs(lock); if (lastCleanedAge) { ptr->next = first; first = ptr; if (ptr->next) ptr->next->prev = ptr; if (!last) { MALLOC_ASSERT(0 == oldest, ASSERT_TEXT); oldest = ptr->age; last = ptr; } } else { // 1st object of such size was released. // Not cache it, and remeber when this occurs // to take into account during cache miss. lastCleanedAge = ptr->age; blockCached = false; } } return blockCached; }
bool LargeObjectCache::CacheBin::cleanAll(ExtMemoryPool *extMemPool) { LargeMemoryBlock *toRelease = NULL; bool released = false; if (last) { MallocMutex::scoped_lock scoped_cs(lock); // double check if (last) { toRelease = first; last = NULL; first = NULL; oldest = 0; } else return false; } released = toRelease; while ( toRelease ) { LargeMemoryBlock *helper = toRelease->next; removeBackRef(toRelease->backRefIdx); extMemPool->backend.putLargeBlock(toRelease); toRelease = helper; } return released; }
bool LargeObjectCacheImpl<Props>:: CacheBin::cleanAll(Backend *backend, BinBitMask *bitMask, int idx) { LargeMemoryBlock *toRelease = NULL; bool released = false; if (last) { MallocMutex::scoped_lock scoped_cs(lock); // double check if (last) { toRelease = first; last = NULL; first = NULL; oldest = 0; cachedSize = 0; if (!usedSize) bitMask->set(idx, false); } else return false; } released = toRelease; while ( toRelease ) { LargeMemoryBlock *helper = toRelease->next; MALLOC_ASSERT(!helper || lessThanWithOverflow(helper->age, toRelease->age), ASSERT_TEXT); backend->returnLargeObject(toRelease); toRelease = helper; } return released; }
LargeMemoryBlock *LargeObjectCacheImpl<Props>::CacheBin:: get(size_t size, uintptr_t currTime, bool *setNonEmpty) { LargeMemoryBlock *result=NULL; { MallocMutex::scoped_lock scoped_cs(lock); forgetOutdatedState(currTime); if (first) { result = first; first = result->next; if (first) first->prev = NULL; else { last = NULL; oldest = 0; } // use moving average with current hit interval intptr_t hitRange = currTime - result->age; meanHitRange = meanHitRange? (meanHitRange + hitRange)/2 : hitRange; cachedSize -= size; } else { if (lastCleanedAge) ageThreshold = Props::OnMissFactor*(currTime - lastCleanedAge); } if (!usedSize) // inform that there are used blocks in the bin *setNonEmpty = true; // subject to later correction, if got cache miss and later allocation failed usedSize += size; lastGet = currTime; } return result; }
bool LargeObjectCacheImpl<Props>::CacheBin:: cleanToThreshold(Backend *backend, BinBitMask *bitMask, uintptr_t currTime, int idx) { LargeMemoryBlock *toRelease = NULL; bool released = false; #if MALLOC_DEBUG uintptr_t nextAge = 0; #endif /* oldest may be more recent then age, that's why cast to signed type was used. age overflow is also processed correctly. */ if (last && (intptr_t)(currTime - oldest) > ageThreshold) { MallocMutex::scoped_lock scoped_cs(lock); // double check if (last && (intptr_t)(currTime - last->age) > ageThreshold) { do { #if MALLOC_DEBUG // check that list ordered MALLOC_ASSERT(!nextAge || lessThanWithOverflow(nextAge, last->age), ASSERT_TEXT); nextAge = last->age; #endif cachedSize -= last->unalignedSize; last = last->prev; } while (last && (intptr_t)(currTime - last->age) > ageThreshold); if (last) { toRelease = last->next; oldest = last->age; last->next = NULL; } else { toRelease = first; first = NULL; oldest = 0; if (!usedSize) bitMask->set(idx, false); } MALLOC_ASSERT( toRelease, ASSERT_TEXT ); lastCleanedAge = toRelease->age; } else return false; } released = toRelease; while ( toRelease ) { LargeMemoryBlock *helper = toRelease->next; backend->returnLargeObject(toRelease); toRelease = helper; } return released; }
bool LargeObjectCache::CacheBin::cleanToThreshold(ExtMemoryPool *extMemPool, uintptr_t currAge) { LargeMemoryBlock *toRelease = NULL; bool released = false; /* oldest may be more recent then age, that's why cast to signed type was used. age overflow is also processed correctly. */ if (last && (intptr_t)(currAge - oldest) > ageThreshold) { MallocMutex::scoped_lock scoped_cs(lock); // double check if (last && (intptr_t)(currAge - last->age) > ageThreshold) { do { last = last->prev; } while (last && (intptr_t)(currAge - last->age) > ageThreshold); if (last) { toRelease = last->next; oldest = last->age; last->next = NULL; } else { toRelease = first; first = NULL; oldest = 0; } MALLOC_ASSERT( toRelease, ASSERT_TEXT ); lastCleanedAge = toRelease->age; } else return false; } released = toRelease; while ( toRelease ) { LargeMemoryBlock *helper = toRelease->next; removeBackRef(toRelease->backRefIdx); extMemPool->backend.putLargeBlock(toRelease); toRelease = helper; } return released; }
LargeMemoryBlock *LargeObjectCache::CacheBin::get(ExtMemoryPool *extMemPool, size_t size) { uintptr_t currAge = extMemPool->loc.cleanupCacheIfNeed(extMemPool); LargeMemoryBlock *result=NULL; { MallocMutex::scoped_lock scoped_cs(lock); if (first) { result = first; first = result->next; if (first) first->prev = NULL; else { last = NULL; oldest = 0; } } else { /* If cache miss occured, set ageThreshold to twice the difference between current time and last time cache was cleaned. */ ageThreshold = 2*(currAge - lastCleanedAge); } } return result; }
LargeMemoryBlock *LargeObjectCacheImpl<Props>::CacheBin:: putList(ExtMemoryPool *extMemPool, LargeMemoryBlock *head, BinBitMask *bitMask, int idx) { int i, num, totalNum; size_t size = head->unalignedSize; LargeMemoryBlock *curr, *tail, *toRelease = NULL; uintptr_t currTime; // we not kept prev pointers during assigning blocks to bins, set them now head->prev = NULL; for (num=1, curr=head; curr->next; num++, curr=curr->next) curr->next->prev = curr; tail = curr; totalNum = num; { MallocMutex::scoped_lock scoped_cs(lock); usedSize -= num*size; // to keep ordering on list, get time under list lock currTime = extMemPool->loc.getCurrTimeRange(num); for (curr=tail, i=0; curr; curr=curr->prev, i++) { curr->age = currTime+i; STAT_increment(getThreadId(), ThreadCommonCounters, cacheLargeObj); } if (!lastCleanedAge) { // 1st object of such size was released. // Not cache it, and remeber when this occurs // to take into account during cache miss. lastCleanedAge = tail->age; toRelease = tail; tail = tail->prev; if (tail) tail->next = NULL; else head = NULL; num--; } if (num) { // add [head;tail] list to cache tail->next = first; if (first) first->prev = tail; first = head; if (!last) { MALLOC_ASSERT(0 == oldest, ASSERT_TEXT); oldest = tail->age; last = tail; } cachedSize += num*size; } /* It's accebtable, if a bin is empty, and we have non-empty in bit mask. So set true in bitmask without lock. It's not acceptable, if a bin is non-empty and we have empty in bitmask. So set false in bitmask under lock. */ // No used object, and nothing in the bin, mark the bin as empty if (!usedSize && !first) bitMask->set(idx, false); } extMemPool->loc.cleanupCacheIfNeededOnRange(totalNum, currTime); if (toRelease) toRelease->prev = toRelease->next = NULL; return toRelease; }