diff --git a/src/kits/support/BlockCache.cpp b/src/kits/support/BlockCache.cpp index 128bdb535d..a23bc74947 100644 --- a/src/kits/support/BlockCache.cpp +++ b/src/kits/support/BlockCache.cpp @@ -1,173 +1,153 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// File Name: BlockCache.cpp -// Author(s): Massimiliano Origgi -// -// Description: Handles a cache of memory blocks of the same size -//------------------------------------------------------------------------------ +/* + * Copyright (c) 2003 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ -// Standard Includes ----------------------------------------------------------- -#include - -// System Includes ------------------------------------------------------------- -#include #include +#include +#include +#include +#include +#define MAGIC1 0x9183f4d9 +#define MAGIC2 0xa6b3c87d -// Private functions -static void * -object_alloc(size_t Size) +// The requirements set by the BeBook's description of the destructor, +// as well as Get() function, allowing the caller to dispose of the +// memory, do not allow to allocate one large block to be used as pool. +// Thus we need to create multiple small ones. +// We maintain a list of free blocks. If more blocks with a size of blockSize +// are allocated than available, the variable fExcessBlocks is used to avoid +// growing the pool of free buffers + +// XXX the BeBook describes the first parameter of the constructor as +// XXX "size_t count", while the original BeOS header uses "size_t cache_size" +// XXX this are very different meanings + +BBlockCache::BBlockCache(size_t cacheSize, + size_t blockSize, + uint32 allocationType) + : fFreeList(0), + fBlockSize(blockSize), + fExcessBlocks(0), + fLocker(), + fAlloc(0), + fFree(0) { - return (void *)(new char[Size]); -} + if (cacheSize < blockSize) + debugger("Error, you can't create a BBlockCache with cacheSize < blockSize\n"); - -static void -object_free(void *Data) -{ - delete[] (int8*)Data; -} - - -static void* -malloc_alloc(size_t Size) -{ - return malloc(Size); -} - - -static void -malloc_free(void *Data) -{ - free(Data); -} - - -// Private structure for cache -struct _Block -{ - _Block(void); - ~_Block(void); - void *Data; - bool InUse; -}; - - -_Block::_Block(void) : - Data(NULL), - InUse(false) -{ -} - - -_Block::~_Block(void) -{ -} - - -BBlockCache::BBlockCache(size_t CacheSize, size_t BlockSize, uint32 Type) - : fCacheSize(CacheSize), - fBlockSize(BlockSize), - fMark(0) -{ - // Setup function pointers based on Type - if(Type == B_OBJECT_CACHE) { - fAlloc = &object_alloc; - fFree = &object_free; - } else { - fAlloc = &malloc_alloc; - fFree = &malloc_free; + switch (allocationType) { + case B_OBJECT_CACHE: + fAlloc = &operator new[]; + fFree = &operator delete[]; + break; + case B_MALLOC_CACHE: + fAlloc = &malloc; + fFree = &free; + break; + default: + debugger("Error, you can't create a BBlockCache with undefined allocationType\n"); } - - // Allocate cache - fCache = (void *)new _Block[CacheSize]; - - for(size_t i = 0; i < CacheSize; i++) - ((_Block *)fCache)[i].Data = fAlloc(BlockSize); -} + // To properly maintain a list of free buffers, a buffer must be + // large enough to contain the _FreeBlock struct that is used. + if (blockSize < sizeof(_FreeBlock)) + blockSize = sizeof(_FreeBlock); + + // create blocks and put them into the free list + int count = cacheSize / blockSize; + for (int i = 0; i < count; i++) { + _FreeBlock *block = reinterpret_cast<_FreeBlock *>(fAlloc(blockSize)); + if (!block) + break; + block->next = fFreeList; + fFreeList = block; + DEBUG_ONLY(block->magic1 = MAGIC1); + DEBUG_ONLY(block->magic2 = MAGIC2 + (uint32)block->next); + } +} BBlockCache::~BBlockCache() { - delete[] (_Block*)fCache; + // walk the free list and deallocate all blocks + fLocker.Lock(); + while (fFreeList) { + ASSERT(fFreeList->magic1 == MAGIC1); + ASSERT(fFreeList->magic2 == MAGIC2 + (uint32)fFreeList->next); + void *pointer = fFreeList; + fFreeList = fFreeList->next; + DEBUG_ONLY(memset(pointer, 0xCC, sizeof(_FreeBlock))); + fFree(pointer); + } + fLocker.Unlock(); } - void * -BBlockCache::Get(size_t BlockSize) +BBlockCache::Get(size_t blockSize) { - BAutolock lock(fLock); - - if(BlockSize != fBlockSize) - return fAlloc(BlockSize); - - _Block *block; - for(size_t i = fMark; i < fCacheSize; i++) { - block = &((_Block *)fCache)[i]; - if(block->InUse == false) { - block->InUse = true; - ++fMark; - if(fMark == fCacheSize) - fMark = 0; - return block->Data; + if (!fLocker.Lock()) + return 0; + void *pointer; + if (blockSize == fBlockSize && fFreeList != 0) { + // we can take a block from the list + ASSERT(fFreeList->magic1 == MAGIC1); + ASSERT(fFreeList->magic2 == MAGIC2 + (uint32)fFreeList->next); + pointer = fFreeList; + fFreeList = fFreeList->next; + DEBUG_ONLY(memset(pointer, 0xCC, sizeof(_FreeBlock))); + } else { + // we need to allocate a new block + if (blockSize == fBlockSize) { + // we now have one more block than wanted + fExcessBlocks++; } + if (blockSize < sizeof(_FreeBlock)) + blockSize = sizeof(_FreeBlock); + pointer = fAlloc(blockSize); + DEBUG_ONLY(if (pointer) memset(pointer, 0xCC, sizeof(_FreeBlock))); } - - if(fMark == 0) - return fAlloc(BlockSize); - - for(size_t i = 0; i < fMark; i++) { - block = &((_Block *)fCache)[i]; - if(block->InUse == false) { - block->InUse = true; - ++fMark; - if(fMark == fCacheSize) - fMark = 0; - return block->Data; - } - } - - return fAlloc(BlockSize); + fLocker.Unlock(); + return pointer; } - -void -BBlockCache::Save(void *Data, size_t BlockSize) +void +BBlockCache::Save(void *pointer, size_t blockSize) { - BAutolock lock(fLock); - - if(BlockSize != fBlockSize) { - fFree(Data); + if (!fLocker.Lock()) return; - } - - _Block *block; - for(size_t i = 0; i < fCacheSize; i++) { - block = &((_Block *)fCache)[i]; - if(block->Data == Data) { - block->InUse = false; - fMark = i; - return; + if (blockSize == fBlockSize && fExcessBlocks <= 0) { + // the block needs to be returned to the cache + _FreeBlock *block = reinterpret_cast<_FreeBlock *>(pointer); + block->next = fFreeList; + fFreeList = block; + DEBUG_ONLY(block->magic1 = MAGIC1); + DEBUG_ONLY(block->magic2 = MAGIC2 + (uint32)block->next); + } else { + // the block needs to be deallocated + if (blockSize == fBlockSize) { + fExcessBlocks--; + ASSERT(fExcessBlocks >= 0); } + DEBUG_ONLY(memset(pointer, 0xCC, sizeof(_FreeBlock))); + fFree(pointer); } - fFree(Data); + fLocker.Unlock(); }