struct LumpCache
{
VtLock *lock;
VtRendez *full;
Lump *free; /* list of available lumps */
u32int allowed; /* total allowable space for packets */
u32int avail; /* remaining space for packets */
u32int now; /* ticks for usage timestamps */
Lump **heads; /* hash table for finding address */
int nheap; /* number of available victims */
Lump **heap; /* heap for locating victims */
int nblocks; /* number of blocks allocated */
Lump *blocks; /* array of block descriptors */
};
static LumpCache lumpCache;
static void delHeap(Lump *db);
static int downHeap(int i, Lump *b);
static void fixHeap(int i, Lump *b);
static int upHeap(int i, Lump *b);
static Lump *bumpLump(void);
last = nil;
for(i = 0; i < nblocks; i++){
b = &lumpCache.blocks[i];
b->type = TWID8;
b->heap = TWID32;
b->lock = vtLockAlloc();
b->next = last;
last = b;
}
lumpCache.free = last;
lumpCache.nheap = 0;
}
Lump*
lookupLump(u8int *score, int type)
{
Lump *b;
u32int h;
h = hashBits(score, HashLog);
/*
* look for the block in the cache
*/
//checkLumpCache();
vtLock(lumpCache.lock);
again:
for(b = lumpCache.heads[h]; b != nil; b = b->next){
if(scoreEq(score, b->score) && type == b->type){
vtLock(stats.lock);
stats.lumpHit++;
vtUnlock(stats.lock);
goto found;
}
}
/*
* missed: locate the block with the oldest second to last use.
* remove it from the heap, and fix up the heap.
*/
while(lumpCache.free == nil){
if(bumpLump() == nil){
logErr(EAdmin, "all lump cache blocks in use");
vtSleep(lumpCache.full);
goto again;
}
}
vtLock(stats.lock);
stats.lumpMiss++;
vtUnlock(stats.lock);
b = lumpCache.free;
lumpCache.free = b->next;
/*
* the new block has no last use, so assume it happens sometime in the middle
ZZZ this is not reasonable
*/
b->used = (b->used2 + lumpCache.now) / 2;
/*
* rechain the block on the correct hash chain
*/
b->next = lumpCache.heads[h];
lumpCache.heads[h] = b;
if(b->next != nil)
b->next->prev = b;
b->prev = nil;
/*
* look for the block in the cache
*/
//checkLumpCache();
vtLock(lumpCache.lock);
again:
/*
* missed: locate the block with the oldest second to last use.
* remove it from the heap, and fix up the heap.
*/
size = packetAllocatedSize(p);
//ZZZ
while(lumpCache.avail < size){
if(bumpLump() == nil){
logErr(EAdmin, "all lump cache blocks in use");
vtSleep(lumpCache.full);
goto again;
}
}
b->data = p;
b->size = size;
lumpCache.avail -= size;
/*
* remove some lump from use and update the free list and counters
*/
static Lump*
bumpLump(void)
{
Lump *b;
u32int h;
/*
* remove blocks until we find one that is unused
* referenced blocks are left in the heap even though
* they can't be scavenged; this is simple a speed optimization
*/
for(;;){
if(lumpCache.nheap == 0)
return nil;
b = lumpCache.heap[0];
delHeap(b);
if(!b->ref){
vtWakeup(lumpCache.full);
break;
}
}
/*
* unchain the block
*/
if(b->prev == nil){
h = hashBits(b->score, HashLog);
if(lumpCache.heads[h] != b)
fatal("bad hash chains in lump cache");
lumpCache.heads[h] = b->next;
}else
b->prev->next = b->next;
if(b->next != nil)
b->next->prev = b->prev;