Example #1
0
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;
}
Example #2
0
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;
}
Example #6
0
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;
}
Example #7
0
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;
}