Check in per Jeremy Rand: Added BBlockCache unit tests.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4522 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -6,6 +6,7 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) bautolock ] ;
|
|||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) blocker ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) blocker ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) bmemoryio ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) bmemoryio ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) bstring ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) bstring ] ;
|
||||||
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) bblockcache ] ;
|
||||||
|
|
||||||
CommonTestLib libsupporttest.so
|
CommonTestLib libsupporttest.so
|
||||||
: SupportKitTestAddon.cpp
|
: SupportKitTestAddon.cpp
|
||||||
@@ -66,6 +67,11 @@ CommonTestLib libsupporttest.so
|
|||||||
StringSearchTest.cpp
|
StringSearchTest.cpp
|
||||||
StringReplaceTest.cpp
|
StringReplaceTest.cpp
|
||||||
|
|
||||||
|
#BBlockCache
|
||||||
|
BlockCacheTest.cpp
|
||||||
|
BlockCacheExerciseTest.cpp
|
||||||
|
BlockCacheConcurrencyTest.cpp
|
||||||
|
|
||||||
: <boot!home!config!lib>libopenbeos.so
|
: <boot!home!config!lib>libopenbeos.so
|
||||||
be stdc++.r4
|
be stdc++.r4
|
||||||
: be stdc++.r4
|
: be stdc++.r4
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "bmemoryio/MemoryIOTest.h"
|
#include "bmemoryio/MemoryIOTest.h"
|
||||||
#include "bmemoryio/MallocIOTest.h"
|
#include "bmemoryio/MallocIOTest.h"
|
||||||
#include "bstring/StringTest.h"
|
#include "bstring/StringTest.h"
|
||||||
|
#include "bblockcache/BlockCacheTest.h"
|
||||||
|
|
||||||
BTestSuite* getTestSuite() {
|
BTestSuite* getTestSuite() {
|
||||||
BTestSuite *suite = new BTestSuite("Support");
|
BTestSuite *suite = new BTestSuite("Support");
|
||||||
@@ -19,6 +20,7 @@ BTestSuite* getTestSuite() {
|
|||||||
suite->addTest("BMemoryIO", MemoryIOTestSuite());
|
suite->addTest("BMemoryIO", MemoryIOTestSuite());
|
||||||
suite->addTest("BMallocIO", MallocIOTestSuite());
|
suite->addTest("BMallocIO", MallocIOTestSuite());
|
||||||
suite->addTest("BString", StringTestSuite());
|
suite->addTest("BString", StringTestSuite());
|
||||||
|
suite->addTest("BBlockCache", BlockCacheTestSuite());
|
||||||
|
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,301 @@
|
|||||||
|
/*
|
||||||
|
$Id: BlockCacheConcurrencyTest.cpp,v 1.1 2003/09/07 11:53:03 bonefish Exp $
|
||||||
|
|
||||||
|
This file tests BBlockCache from multiple threads to ensure there are
|
||||||
|
no concurrency problems.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "BlockCacheConcurrencyTest.h"
|
||||||
|
#include "ThreadedTestCaller.h"
|
||||||
|
#include <BlockCache.h>
|
||||||
|
#include <List.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::BlockCacheConcurrencyTest()
|
||||||
|
* Descr: This method is the only constructor for the BlockCacheConcurrencyTest
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
BlockCacheConcurrencyTest::BlockCacheConcurrencyTest(std::string name) :
|
||||||
|
BThreadedTestCase(name),
|
||||||
|
theObjCache(NULL),
|
||||||
|
theMallocCache(NULL),
|
||||||
|
numBlocksInCache(128),
|
||||||
|
sizeOfBlocksInCache(23),
|
||||||
|
sizeOfNonCacheBlocks(29)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest()
|
||||||
|
* Descr: This method is the destructor for the BlockCacheConcurrencyTest class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::setUp()
|
||||||
|
* Descr: This method creates a couple of BBlockCache instances to perform
|
||||||
|
* tests on. One uses new/delete and the other uses malloc/free
|
||||||
|
* on its blocks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::setUp(void)
|
||||||
|
{
|
||||||
|
theObjCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache,
|
||||||
|
B_OBJECT_CACHE);
|
||||||
|
theMallocCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache,
|
||||||
|
B_MALLOC_CACHE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::tearDown()
|
||||||
|
* Descr: This method cleans up the BBlockCache instances which were tested.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::tearDown(void)
|
||||||
|
{
|
||||||
|
delete theObjCache;
|
||||||
|
delete theMallocCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::GetBlock()
|
||||||
|
* Descr: This method returns a pointer from the BBlockCache, checking
|
||||||
|
* the value before passing it to the caller.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void *BlockCacheConcurrencyTest::GetBlock(BBlockCache *theCache,
|
||||||
|
size_t blockSize,
|
||||||
|
thread_id theThread,
|
||||||
|
BList *cacheList,
|
||||||
|
BList *nonCacheList)
|
||||||
|
{
|
||||||
|
void *thePtr = theCache->Get(blockSize);
|
||||||
|
|
||||||
|
// The new block should not already be used by this thread.
|
||||||
|
assert(!cacheList->HasItem(thePtr));
|
||||||
|
assert(!nonCacheList->HasItem(thePtr));
|
||||||
|
|
||||||
|
// Add the block to the list of blocks used by this thread.
|
||||||
|
if (blockSize == sizeOfBlocksInCache) {
|
||||||
|
assert(cacheList->AddItem(thePtr));
|
||||||
|
} else {
|
||||||
|
assert(nonCacheList->AddItem(thePtr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the thread id at the start of the block for future
|
||||||
|
// reference.
|
||||||
|
*((thread_id *)thePtr) = theThread;
|
||||||
|
return(thePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::SavedCacheBlock()
|
||||||
|
* Descr: This method passes the pointer back to the BBlockCache
|
||||||
|
* and checks the sanity of the lists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::SaveBlock(BBlockCache *theCache,
|
||||||
|
void *thePtr,
|
||||||
|
size_t blockSize,
|
||||||
|
thread_id theThread,
|
||||||
|
BList *cacheList,
|
||||||
|
BList *nonCacheList)
|
||||||
|
{
|
||||||
|
// The block being returned to the cache should still have
|
||||||
|
// the thread id of this thread in it, or some other thread has
|
||||||
|
// perhaps manipulated this block which would indicate a
|
||||||
|
// concurrency problem.
|
||||||
|
assert(*((thread_id *)thePtr) == theThread);
|
||||||
|
|
||||||
|
// Remove the item from the appropriate list and confirm it isn't
|
||||||
|
// on the other list for some reason.
|
||||||
|
if (blockSize == sizeOfBlocksInCache) {
|
||||||
|
assert(cacheList->RemoveItem(thePtr));
|
||||||
|
assert(!nonCacheList->HasItem(thePtr));
|
||||||
|
} else {
|
||||||
|
assert(!cacheList->HasItem(thePtr));
|
||||||
|
assert(nonCacheList->RemoveItem(thePtr));
|
||||||
|
}
|
||||||
|
theCache->Save(thePtr, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::FreeBlock()
|
||||||
|
* Descr: This method frees the block directly using delete[] or free(),
|
||||||
|
* checking the sanity of the lists as it does the operation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::FreeBlock(void *thePtr,
|
||||||
|
size_t blockSize,
|
||||||
|
bool isMallocTest,
|
||||||
|
thread_id theThread,
|
||||||
|
BList *cacheList,
|
||||||
|
BList *nonCacheList)
|
||||||
|
{
|
||||||
|
// The block being returned to the cache should still have
|
||||||
|
// the thread id of this thread in it, or some other thread has
|
||||||
|
// perhaps manipulated this block which would indicate a
|
||||||
|
// concurrency problem.
|
||||||
|
assert(*((thread_id *)thePtr) == theThread);
|
||||||
|
|
||||||
|
// Remove the item from the appropriate list and confirm it isn't
|
||||||
|
// on the other list for some reason.
|
||||||
|
if (blockSize == sizeOfBlocksInCache) {
|
||||||
|
assert(cacheList->RemoveItem(thePtr));
|
||||||
|
assert(!nonCacheList->HasItem(thePtr));
|
||||||
|
} else {
|
||||||
|
assert(!cacheList->HasItem(thePtr));
|
||||||
|
assert(nonCacheList->RemoveItem(thePtr));
|
||||||
|
}
|
||||||
|
if (isMallocTest) {
|
||||||
|
free(thePtr);
|
||||||
|
} else {
|
||||||
|
delete[] thePtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::TestBlockCache()
|
||||||
|
* Descr: This method performs the tests on BBlockCache. It is
|
||||||
|
* called by 6 threads concurrently. Three of them are
|
||||||
|
* operating on the B_OBJECT_CACHE instance of BBlockCache
|
||||||
|
* and the other three are operating on the B_MALLOC_CACHE
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* The goal of this method is to perform a series of get,
|
||||||
|
* save and free operations on block from the cache using
|
||||||
|
* "cache size" and "non-cache size" blocks. Also, at the
|
||||||
|
* end of this method, all blocks unfreed by this method are
|
||||||
|
* freed to avoid a memory leak.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::TestBlockCache(BBlockCache *theCache,
|
||||||
|
bool isMallocTest)
|
||||||
|
{
|
||||||
|
BList cacheList;
|
||||||
|
BList nonCacheList;
|
||||||
|
thread_id theThread = find_thread(NULL);
|
||||||
|
|
||||||
|
// Do everything eight times to ensure the test runs long
|
||||||
|
// enough to check for concurrency problems.
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
// Perform a series of gets, saves and frees
|
||||||
|
for (int i = 0; i < numBlocksInCache / 2; i++) {
|
||||||
|
GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
|
||||||
|
GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
|
||||||
|
GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
|
||||||
|
GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
|
||||||
|
|
||||||
|
SaveBlock(theCache, cacheList.ItemAt(cacheList.CountItems() / 2),
|
||||||
|
sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
|
||||||
|
SaveBlock(theCache, nonCacheList.ItemAt(nonCacheList.CountItems() / 2),
|
||||||
|
sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
|
||||||
|
|
||||||
|
GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
|
||||||
|
GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
|
||||||
|
GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
|
||||||
|
GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
|
||||||
|
|
||||||
|
FreeBlock(cacheList.ItemAt(cacheList.CountItems() / 2),
|
||||||
|
sizeOfBlocksInCache, isMallocTest, theThread, &cacheList, &nonCacheList);
|
||||||
|
FreeBlock(nonCacheList.ItemAt(nonCacheList.CountItems() / 2),
|
||||||
|
sizeOfNonCacheBlocks, isMallocTest, theThread, &cacheList, &nonCacheList);
|
||||||
|
}
|
||||||
|
bool performFree = false;
|
||||||
|
// Free or save (every other block) for all "cache sized" blocks.
|
||||||
|
while (!cacheList.IsEmpty()) {
|
||||||
|
if (performFree) {
|
||||||
|
FreeBlock(cacheList.LastItem(), sizeOfBlocksInCache, isMallocTest, theThread, &cacheList,
|
||||||
|
&nonCacheList);
|
||||||
|
} else {
|
||||||
|
SaveBlock(theCache, cacheList.LastItem(), sizeOfBlocksInCache, theThread, &cacheList,
|
||||||
|
&nonCacheList);
|
||||||
|
}
|
||||||
|
performFree = !performFree;
|
||||||
|
}
|
||||||
|
// Free or save (every other block) for all "non-cache sized" blocks.
|
||||||
|
while (!nonCacheList.IsEmpty()) {
|
||||||
|
if (performFree) {
|
||||||
|
FreeBlock(nonCacheList.LastItem(), sizeOfNonCacheBlocks, isMallocTest, theThread, &cacheList,
|
||||||
|
&nonCacheList);
|
||||||
|
} else {
|
||||||
|
SaveBlock(theCache, nonCacheList.LastItem(), sizeOfNonCacheBlocks, theThread, &cacheList,
|
||||||
|
&nonCacheList);
|
||||||
|
}
|
||||||
|
performFree = !performFree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::TestThreadMalloc()
|
||||||
|
* Descr: This method passes the BBlockCache instance to TestBlockCache()
|
||||||
|
* where the instance will be tested.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::TestThreadMalloc(void)
|
||||||
|
{
|
||||||
|
TestBlockCache(theMallocCache, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::TestThreadObj()
|
||||||
|
* Descr: This method passes the BBlockCache instance to TestBlockCache()
|
||||||
|
* where the instance will be tested.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheConcurrencyTest::TestThreadObj(void)
|
||||||
|
{
|
||||||
|
TestBlockCache(theObjCache, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheConcurrencyTest::suite()
|
||||||
|
* Descr: This static member function returns a test caller for performing
|
||||||
|
* the "BlockCacheConcurrencyTest" test. The test caller
|
||||||
|
* is created as a ThreadedTestCaller with six independent threads.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
CppUnit::Test *BlockCacheConcurrencyTest::suite(void)
|
||||||
|
{
|
||||||
|
typedef BThreadedTestCaller <BlockCacheConcurrencyTest >
|
||||||
|
BlockCacheConcurrencyTestCaller;
|
||||||
|
|
||||||
|
BlockCacheConcurrencyTest *theTest = new BlockCacheConcurrencyTest("");
|
||||||
|
BlockCacheConcurrencyTestCaller *threadedTest = new BlockCacheConcurrencyTestCaller("BBlockCache::Concurrency Test", theTest);
|
||||||
|
threadedTest->addThread("A", &BlockCacheConcurrencyTest::TestThreadObj);
|
||||||
|
threadedTest->addThread("B", &BlockCacheConcurrencyTest::TestThreadObj);
|
||||||
|
threadedTest->addThread("C", &BlockCacheConcurrencyTest::TestThreadObj);
|
||||||
|
threadedTest->addThread("D", &BlockCacheConcurrencyTest::TestThreadMalloc);
|
||||||
|
threadedTest->addThread("E", &BlockCacheConcurrencyTest::TestThreadMalloc);
|
||||||
|
threadedTest->addThread("F", &BlockCacheConcurrencyTest::TestThreadMalloc);
|
||||||
|
return(threadedTest);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
$Id: BlockCacheConcurrencyTest.h,v 1.1 2003/09/07 11:53:03 bonefish Exp $
|
||||||
|
|
||||||
|
This file defines a class for testing BBlockCache
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BlockCacheConcurrencyTest_H
|
||||||
|
#define BlockCacheConcurrencyTest_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "ThreadedTestCase.h"
|
||||||
|
#include <string>
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BBlockCache;
|
||||||
|
class BList;
|
||||||
|
|
||||||
|
|
||||||
|
class BlockCacheConcurrencyTest : public BThreadedTestCase {
|
||||||
|
|
||||||
|
private:
|
||||||
|
BBlockCache *theObjCache;
|
||||||
|
BBlockCache *theMallocCache;
|
||||||
|
int numBlocksInCache;
|
||||||
|
size_t sizeOfBlocksInCache;
|
||||||
|
size_t sizeOfNonCacheBlocks;
|
||||||
|
|
||||||
|
void *GetBlock(BBlockCache *theCache, size_t blockSize,
|
||||||
|
thread_id theThread, BList *cacheList, BList *nonCacheList);
|
||||||
|
void SaveBlock(BBlockCache *theCache, void *, size_t blockSize,
|
||||||
|
thread_id theThread, BList *cacheList, BList *nonCacheList);
|
||||||
|
void FreeBlock(void *, size_t blockSize, bool isMallocTest,
|
||||||
|
thread_id theThread, BList *cacheList,
|
||||||
|
BList *nonCacheList);
|
||||||
|
void TestBlockCache(BBlockCache *theCache, bool isMallocTest);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Test *suite(void);
|
||||||
|
void TestThreadObj(void);
|
||||||
|
void TestThreadMalloc(void);
|
||||||
|
virtual void setUp(void);
|
||||||
|
virtual void tearDown(void);
|
||||||
|
BlockCacheConcurrencyTest(std::string);
|
||||||
|
virtual ~BlockCacheConcurrencyTest();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
$Id: BlockCacheExerciseTest.cpp,v 1.1 2003/09/07 11:53:03 bonefish Exp $
|
||||||
|
|
||||||
|
This file tests basic functionality of BBlockCache.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "BlockCacheExerciseTest.h"
|
||||||
|
#include "cppunit/TestCaller.h"
|
||||||
|
#include <BlockCache.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::BlockCacheExerciseTest()
|
||||||
|
* Descr: This method is the only constructor for the BlockCacheExerciseTest
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
BlockCacheExerciseTest::BlockCacheExerciseTest(std::string name) :
|
||||||
|
TestCase(name),
|
||||||
|
theCache(NULL),
|
||||||
|
numBlocksInCache(0),
|
||||||
|
sizeOfBlocksInCache(0),
|
||||||
|
sizeOfNonCacheBlocks(0),
|
||||||
|
isMallocTest(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::~BlockCacheExerciseTest()
|
||||||
|
* Descr: This method is the destructor for the BlockCacheExerciseTest class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
BlockCacheExerciseTest::~BlockCacheExerciseTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::TestBlockCache()
|
||||||
|
* Descr: This method performs the tests on a particular BBlockCache.
|
||||||
|
* The goal of the method is to perform the following basic
|
||||||
|
* sequence:
|
||||||
|
* 1. Get (numBlocksInCache + 10) blocks of "cache size" from the
|
||||||
|
* BBlockCache. By doing this, we can guarantee that the cache
|
||||||
|
* has been exhausted and some elements are allocated to
|
||||||
|
* satisfy the caller. Then, they are all returned to the
|
||||||
|
* cache in most recent block to oldest block order. This
|
||||||
|
* confirms that regardless whether the block was initially
|
||||||
|
* part of the cache or created because the cache was empty,
|
||||||
|
* once it is returned to the cache, it is just placed on the
|
||||||
|
* cache. Imagine a cache of 4 blocks. Initally, the cache
|
||||||
|
* has the following blocks:
|
||||||
|
* A, B, C, D
|
||||||
|
* If five blocks are gotten from the cache, the caller will get
|
||||||
|
* A, B, C, D, E
|
||||||
|
* However, E wasn't initially part of the cache. It was allocated
|
||||||
|
* dynamically to satisfy the caller's request because the cache
|
||||||
|
* was empty. Now, if they are returned in the order E, D, C, B, A
|
||||||
|
* the cache will have the following blocks available in it:
|
||||||
|
* B, C, D, E
|
||||||
|
* When A is returned, the cache will find there is no more room
|
||||||
|
* for more free blocks and it will be freed. This is the
|
||||||
|
* behaviour which is confirmed initially.
|
||||||
|
*
|
||||||
|
* 2. After this is done, the cache is just "exercised". The following
|
||||||
|
* is done "numBlocksInCache" times:
|
||||||
|
* - 4 "cache sized" blocks are gotten from the cache
|
||||||
|
* - 4 "non-cache sized" blocks are gotten from the cache
|
||||||
|
* - 1 "cache sized" block is returned back to the cache
|
||||||
|
* - 1 "non-cache sized" block is returned back to the cache
|
||||||
|
* - 1 "cache sized" block is freed and not returned to the cache
|
||||||
|
* - 1 "non-cache sized" block is freed and not returned to the
|
||||||
|
* cache (but even if given to the BBlockCache, it would just
|
||||||
|
* be freed anyhow)
|
||||||
|
* What this means is that everytime through the loop, 2 "cache sized"
|
||||||
|
* and 2 "non-cache sized" blocks are kept in memory. At the end,
|
||||||
|
* 2 * numBlocksInCache items of size "cache size" and "non cache size"
|
||||||
|
* will exist.
|
||||||
|
*
|
||||||
|
* Then, numBlocksInCache / 4 items are returned to the cache and
|
||||||
|
* numBlocksInCache / 4 are freed. This ensures at the end of the
|
||||||
|
* test that there are some available blocks in the cache.
|
||||||
|
*
|
||||||
|
* The sum total of these actions test the BBlockCache.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheExerciseTest::TestBlockCache(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
// First get all items from the cache plus ten more
|
||||||
|
for (int i = 0; i < numBlocksInCache + 10; i++) {
|
||||||
|
GetBlock(sizeOfBlocksInCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put them all back in reverse order to confirm 1 from above
|
||||||
|
while (!usedList.IsEmpty()) {
|
||||||
|
SaveBlock(usedList.LastItem(), sizeOfBlocksInCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a bunch of blocks and send some back to the cache
|
||||||
|
// to confirm 2 from above.
|
||||||
|
for (int i = 0; i < numBlocksInCache; i++) {
|
||||||
|
GetBlock(sizeOfBlocksInCache);
|
||||||
|
GetBlock(sizeOfBlocksInCache);
|
||||||
|
GetBlock(sizeOfNonCacheBlocks);
|
||||||
|
GetBlock(sizeOfNonCacheBlocks);
|
||||||
|
|
||||||
|
// We send one back from the middle of the lists so
|
||||||
|
// the most recent block is not the one returned.
|
||||||
|
SaveBlock(usedList.ItemAt(usedList.CountItems() / 2),
|
||||||
|
sizeOfBlocksInCache);
|
||||||
|
SaveBlock(nonCacheList.ItemAt(nonCacheList.CountItems() / 2),
|
||||||
|
sizeOfNonCacheBlocks);
|
||||||
|
|
||||||
|
GetBlock(sizeOfBlocksInCache);
|
||||||
|
GetBlock(sizeOfBlocksInCache);
|
||||||
|
GetBlock(sizeOfNonCacheBlocks);
|
||||||
|
GetBlock(sizeOfNonCacheBlocks);
|
||||||
|
|
||||||
|
// We free one from the middle of the lists so the
|
||||||
|
// most recent block is not the one freed.
|
||||||
|
FreeBlock(usedList.ItemAt(usedList.CountItems() / 2),
|
||||||
|
sizeOfBlocksInCache);
|
||||||
|
FreeBlock(nonCacheList.ItemAt(nonCacheList.CountItems() / 2),
|
||||||
|
sizeOfNonCacheBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, send some blocks back to the cache and free some blocks
|
||||||
|
// so the cache is not empty at the end of the test.
|
||||||
|
for (int i = 0; i < numBlocksInCache / 4; i++) {
|
||||||
|
// Return the blocks which are 2/3s of the way through the
|
||||||
|
// lists.
|
||||||
|
SaveBlock(usedList.ItemAt(usedList.CountItems() * 2 / 3),
|
||||||
|
sizeOfBlocksInCache);
|
||||||
|
SaveBlock(nonCacheList.ItemAt(nonCacheList.CountItems() * 2 / 3),
|
||||||
|
sizeOfNonCacheBlocks);
|
||||||
|
|
||||||
|
// Free the blocks which are 1/3 of the way through the lists.
|
||||||
|
FreeBlock(usedList.ItemAt(usedList.CountItems() / 3),
|
||||||
|
sizeOfBlocksInCache);
|
||||||
|
FreeBlock(nonCacheList.ItemAt(nonCacheList.CountItems() / 3),
|
||||||
|
sizeOfNonCacheBlocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::BuildLists()
|
||||||
|
* Descr: This method gets all of the blocks from the cache in order to
|
||||||
|
* track them for future tests. The assumption here is that the
|
||||||
|
* blocks are not deallocated and reallocated in the implementation
|
||||||
|
* of BBlockCache. Given the purpose is to provide a cheap way to
|
||||||
|
* access allocated memory without resorting to malloc()'s or new's,
|
||||||
|
* this should be a fair assumption.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheExerciseTest::BuildLists(void)
|
||||||
|
{
|
||||||
|
freeList.MakeEmpty();
|
||||||
|
usedList.MakeEmpty();
|
||||||
|
nonCacheList.MakeEmpty();
|
||||||
|
|
||||||
|
for(int i = 0; i < numBlocksInCache; i++) {
|
||||||
|
freeList.AddItem(theCache->Get(sizeOfBlocksInCache));
|
||||||
|
}
|
||||||
|
for(int i = 0; i < numBlocksInCache; i++) {
|
||||||
|
theCache->Save(freeList.ItemAt(i), sizeOfBlocksInCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::GetBlock()
|
||||||
|
* Descr: This method returns a pointer from the BBlockCache, checking
|
||||||
|
* the value before passing it to the caller.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void *BlockCacheExerciseTest::GetBlock(size_t blockSize)
|
||||||
|
{
|
||||||
|
void *thePtr = theCache->Get(blockSize);
|
||||||
|
|
||||||
|
// This new pointer should not be one which we already
|
||||||
|
// have from the BBlockCache which we haven't given back
|
||||||
|
// yet.
|
||||||
|
assert(!usedList.HasItem(thePtr));
|
||||||
|
assert(!nonCacheList.HasItem(thePtr));
|
||||||
|
|
||||||
|
if (blockSize == sizeOfBlocksInCache) {
|
||||||
|
// If this block was one which could have come from the
|
||||||
|
// cache and there are free items on the cache, it
|
||||||
|
// should be one of those free blocks.
|
||||||
|
if (freeList.CountItems() > 0) {
|
||||||
|
assert(freeList.RemoveItem(thePtr));
|
||||||
|
}
|
||||||
|
assert(usedList.AddItem(thePtr));
|
||||||
|
} else {
|
||||||
|
// A "non-cache sized" block should never come from the
|
||||||
|
// free list.
|
||||||
|
assert(!freeList.HasItem(thePtr));
|
||||||
|
assert(nonCacheList.AddItem(thePtr));
|
||||||
|
}
|
||||||
|
return(thePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::SavedCacheBlock()
|
||||||
|
* Descr: This method passes the pointer back to the BBlockCache
|
||||||
|
* and checks the sanity of the lists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheExerciseTest::SaveBlock(void *thePtr, size_t blockSize)
|
||||||
|
{
|
||||||
|
// The memory block being returned to the cache should
|
||||||
|
// not already be free.
|
||||||
|
assert(!freeList.HasItem(thePtr));
|
||||||
|
|
||||||
|
if (blockSize == sizeOfBlocksInCache) {
|
||||||
|
// If there is room on the free list, when this block
|
||||||
|
// is returned to the cache, it will be put on the
|
||||||
|
// free list. Therefore we will also track it as
|
||||||
|
// a free block on the cache.
|
||||||
|
if (freeList.CountItems() < numBlocksInCache) {
|
||||||
|
assert(freeList.AddItem(thePtr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This block should not be on the non-cache list but it
|
||||||
|
// should be on the used list.
|
||||||
|
assert(!nonCacheList.HasItem(thePtr));
|
||||||
|
assert(usedList.RemoveItem(thePtr));
|
||||||
|
} else {
|
||||||
|
// This block should not be on the used list but it should
|
||||||
|
// be on the non-cache list.
|
||||||
|
assert(!usedList.HasItem(thePtr));
|
||||||
|
assert(nonCacheList.RemoveItem(thePtr));
|
||||||
|
}
|
||||||
|
theCache->Save(thePtr, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::FreeBlock()
|
||||||
|
* Descr: This method frees the block directly using delete[] or free(),
|
||||||
|
* checking the sanity of the lists as it does the operation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheExerciseTest::FreeBlock(void *thePtr, size_t blockSize)
|
||||||
|
{
|
||||||
|
// The block being freed should not already have been
|
||||||
|
// returned to the cache.
|
||||||
|
assert(!freeList.HasItem(thePtr));
|
||||||
|
|
||||||
|
if (blockSize == sizeOfBlocksInCache) {
|
||||||
|
// This block should not be on the non-cache list but it
|
||||||
|
// should be on the used list.
|
||||||
|
assert(!nonCacheList.HasItem(thePtr));
|
||||||
|
assert(usedList.RemoveItem(thePtr));
|
||||||
|
} else {
|
||||||
|
// This block should not be on the used list but it should
|
||||||
|
// be on the non-cache list.
|
||||||
|
assert(!usedList.HasItem(thePtr));
|
||||||
|
assert(nonCacheList.RemoveItem(thePtr));
|
||||||
|
}
|
||||||
|
if (isMallocTest) {
|
||||||
|
free(thePtr);
|
||||||
|
} else {
|
||||||
|
delete[] thePtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::PerformTest()
|
||||||
|
* Descr: This method performs the tests on a series of BBlockCache
|
||||||
|
* instances. It performs the tests with a series of
|
||||||
|
* block sizes and numbers of blocks. Also, it does the
|
||||||
|
* test using a B_OBJECT_CACHE and B_MALLOC_CACHE type
|
||||||
|
* cache. For each individual BBlockCache to be tested, it:
|
||||||
|
* 1. Queries the cache to find out the blocks which are
|
||||||
|
* on it.
|
||||||
|
* 2. Performs the test.
|
||||||
|
* 3. Frees all blocks left after the test to prevent
|
||||||
|
* memory leaks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCacheExerciseTest::PerformTest(void)
|
||||||
|
{
|
||||||
|
for (numBlocksInCache = 8; numBlocksInCache < 513; numBlocksInCache *= 2) {
|
||||||
|
for (sizeOfBlocksInCache = 13; sizeOfBlocksInCache < 9478; sizeOfBlocksInCache *= 3) {
|
||||||
|
|
||||||
|
// To test getting blocks which are not from the cache,
|
||||||
|
// we will get blocks of 6 bytes less than the size of
|
||||||
|
// the blocks on the cache.
|
||||||
|
sizeOfNonCacheBlocks = sizeOfBlocksInCache - 6;
|
||||||
|
|
||||||
|
isMallocTest = false;
|
||||||
|
theCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache, B_OBJECT_CACHE);
|
||||||
|
assert(theCache != NULL);
|
||||||
|
|
||||||
|
// Query the cache and determine the blocks in it.
|
||||||
|
BuildLists();
|
||||||
|
// Perform the test on this instance.
|
||||||
|
TestBlockCache();
|
||||||
|
delete theCache;
|
||||||
|
// Clean up remaining memory.
|
||||||
|
while (!usedList.IsEmpty()) {
|
||||||
|
FreeBlock(usedList.LastItem(), sizeOfBlocksInCache);
|
||||||
|
}
|
||||||
|
while (!nonCacheList.IsEmpty()) {
|
||||||
|
FreeBlock(nonCacheList.LastItem(), sizeOfNonCacheBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
isMallocTest = true;
|
||||||
|
theCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache, B_MALLOC_CACHE);
|
||||||
|
assert(theCache != NULL);
|
||||||
|
|
||||||
|
// Query the cache and determine the blocks in it.
|
||||||
|
BuildLists();
|
||||||
|
// Perform the test on this instance.
|
||||||
|
TestBlockCache();
|
||||||
|
delete theCache;
|
||||||
|
// Clean up remaining memory.
|
||||||
|
while (!usedList.IsEmpty()) {
|
||||||
|
FreeBlock(usedList.LastItem(), sizeOfBlocksInCache);
|
||||||
|
}
|
||||||
|
while (!nonCacheList.IsEmpty()) {
|
||||||
|
FreeBlock(nonCacheList.LastItem(), sizeOfNonCacheBlocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method: BlockCacheExerciseTest::suite()
|
||||||
|
* Descr: This static member function returns a test caller for performing
|
||||||
|
* the "BlockCacheExerciseTest" test.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
CppUnit::Test *BlockCacheExerciseTest::suite(void)
|
||||||
|
{
|
||||||
|
typedef CppUnit::TestCaller<BlockCacheExerciseTest>
|
||||||
|
BlockCacheExerciseTestCaller;
|
||||||
|
|
||||||
|
return(new BlockCacheExerciseTestCaller("BBlockCache::Exercise Test", &BlockCacheExerciseTest::PerformTest));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
$Id: BlockCacheExerciseTest.h,v 1.1 2003/09/07 11:53:03 bonefish Exp $
|
||||||
|
|
||||||
|
This file defines a class for performing tests on the BBlockCache class.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BlockCacheExerciseTest_H
|
||||||
|
#define BlockCacheExerciseTest_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "cppunit/TestCase.h"
|
||||||
|
#include <List.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BBlockCache;
|
||||||
|
|
||||||
|
|
||||||
|
class BlockCacheExerciseTest : public CppUnit::TestCase {
|
||||||
|
|
||||||
|
private:
|
||||||
|
BBlockCache *theCache;
|
||||||
|
int numBlocksInCache;
|
||||||
|
size_t sizeOfBlocksInCache;
|
||||||
|
size_t sizeOfNonCacheBlocks;
|
||||||
|
|
||||||
|
bool isMallocTest;
|
||||||
|
|
||||||
|
BList freeList;
|
||||||
|
BList usedList;
|
||||||
|
BList nonCacheList;
|
||||||
|
|
||||||
|
void BuildLists(void);
|
||||||
|
void *GetBlock(size_t blockSize);
|
||||||
|
void SaveBlock(void *, size_t blockSize);
|
||||||
|
void FreeBlock(void *, size_t blockSize);
|
||||||
|
void TestBlockCache(void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
public:
|
||||||
|
static CppUnit::Test *suite(void);
|
||||||
|
BlockCacheExerciseTest(std::string = "");
|
||||||
|
virtual ~BlockCacheExerciseTest();
|
||||||
|
virtual void PerformTest(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
$Id: BlockCacheTest.cpp,v 1.1 2003/09/07 11:53:03 bonefish Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "cppunit/Test.h"
|
||||||
|
#include "cppunit/TestSuite.h"
|
||||||
|
#include "BlockCacheExerciseTest.h"
|
||||||
|
#include "BlockCacheConcurrencyTest.h"
|
||||||
|
|
||||||
|
|
||||||
|
CppUnit::Test* BlockCacheTestSuite()
|
||||||
|
{
|
||||||
|
CppUnit::TestSuite *testSuite = new CppUnit::TestSuite();
|
||||||
|
|
||||||
|
testSuite->addTest(BlockCacheExerciseTest::suite());
|
||||||
|
testSuite->addTest(BlockCacheConcurrencyTest::suite());
|
||||||
|
|
||||||
|
return testSuite;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _blockcache_test_h_
|
||||||
|
#define _blockcache_test_h_
|
||||||
|
|
||||||
|
class CppUnit::Test;
|
||||||
|
|
||||||
|
CppUnit::Test* BlockCacheTestSuite();
|
||||||
|
|
||||||
|
#endif // _blockcache_test_h_
|
||||||
Reference in New Issue
Block a user