1 /* 2 $Id: BlockCacheConcurrencyTest.cpp,v 1.1 2003/09/07 11:53:03 bonefish Exp $ 3 4 This file tests BBlockCache from multiple threads to ensure there are 5 no concurrency problems. 6 7 */ 8 9 10 #include "BlockCacheConcurrencyTest.h" 11 #include "ThreadedTestCaller.h" 12 #include <BlockCache.h> 13 #include <List.h> 14 15 16 /* 17 * Method: BlockCacheConcurrencyTest::BlockCacheConcurrencyTest() 18 * Descr: This method is the only constructor for the BlockCacheConcurrencyTest 19 * class. 20 */ 21 22 23 BlockCacheConcurrencyTest::BlockCacheConcurrencyTest(std::string name) : 24 BThreadedTestCase(name), 25 theObjCache(NULL), 26 theMallocCache(NULL), 27 numBlocksInCache(128), 28 sizeOfBlocksInCache(23), 29 sizeOfNonCacheBlocks(29) 30 { 31 } 32 33 34 /* 35 * Method: BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest() 36 * Descr: This method is the destructor for the BlockCacheConcurrencyTest class. 37 */ 38 39 40 BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest() 41 { 42 } 43 44 45 /* 46 * Method: BlockCacheConcurrencyTest::setUp() 47 * Descr: This method creates a couple of BBlockCache instances to perform 48 * tests on. One uses new/delete and the other uses malloc/free 49 * on its blocks. 50 */ 51 52 53 void BlockCacheConcurrencyTest::setUp(void) 54 { 55 theObjCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache, 56 B_OBJECT_CACHE); 57 theMallocCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache, 58 B_MALLOC_CACHE); 59 } 60 61 62 /* 63 * Method: BlockCacheConcurrencyTest::tearDown() 64 * Descr: This method cleans up the BBlockCache instances which were tested. 65 */ 66 67 68 void BlockCacheConcurrencyTest::tearDown(void) 69 { 70 delete theObjCache; 71 delete theMallocCache; 72 } 73 74 75 /* 76 * Method: BlockCacheConcurrencyTest::GetBlock() 77 * Descr: This method returns a pointer from the BBlockCache, checking 78 * the value before passing it to the caller. 79 */ 80 81 82 void *BlockCacheConcurrencyTest::GetBlock(BBlockCache *theCache, 83 size_t blockSize, 84 thread_id theThread, 85 BList *cacheList, 86 BList *nonCacheList) 87 { 88 void *thePtr = theCache->Get(blockSize); 89 90 // The new block should not already be used by this thread. 91 assert(!cacheList->HasItem(thePtr)); 92 assert(!nonCacheList->HasItem(thePtr)); 93 94 // Add the block to the list of blocks used by this thread. 95 if (blockSize == sizeOfBlocksInCache) { 96 assert(cacheList->AddItem(thePtr)); 97 } else { 98 assert(nonCacheList->AddItem(thePtr)); 99 } 100 101 // Store the thread id at the start of the block for future 102 // reference. 103 *((thread_id *)thePtr) = theThread; 104 return(thePtr); 105 } 106 107 108 /* 109 * Method: BlockCacheConcurrencyTest::SavedCacheBlock() 110 * Descr: This method passes the pointer back to the BBlockCache 111 * and checks the sanity of the lists. 112 */ 113 114 115 void BlockCacheConcurrencyTest::SaveBlock(BBlockCache *theCache, 116 void *thePtr, 117 size_t blockSize, 118 thread_id theThread, 119 BList *cacheList, 120 BList *nonCacheList) 121 { 122 // The block being returned to the cache should still have 123 // the thread id of this thread in it, or some other thread has 124 // perhaps manipulated this block which would indicate a 125 // concurrency problem. 126 assert(*((thread_id *)thePtr) == theThread); 127 128 // Remove the item from the appropriate list and confirm it isn't 129 // on the other list for some reason. 130 if (blockSize == sizeOfBlocksInCache) { 131 assert(cacheList->RemoveItem(thePtr)); 132 assert(!nonCacheList->HasItem(thePtr)); 133 } else { 134 assert(!cacheList->HasItem(thePtr)); 135 assert(nonCacheList->RemoveItem(thePtr)); 136 } 137 theCache->Save(thePtr, blockSize); 138 } 139 140 141 /* 142 * Method: BlockCacheConcurrencyTest::FreeBlock() 143 * Descr: This method frees the block directly using delete[] or free(), 144 * checking the sanity of the lists as it does the operation. 145 */ 146 147 148 void BlockCacheConcurrencyTest::FreeBlock(void *thePtr, 149 size_t blockSize, 150 bool isMallocTest, 151 thread_id theThread, 152 BList *cacheList, 153 BList *nonCacheList) 154 { 155 // The block being returned to the cache should still have 156 // the thread id of this thread in it, or some other thread has 157 // perhaps manipulated this block which would indicate a 158 // concurrency problem. 159 assert(*((thread_id *)thePtr) == theThread); 160 161 // Remove the item from the appropriate list and confirm it isn't 162 // on the other list for some reason. 163 if (blockSize == sizeOfBlocksInCache) { 164 assert(cacheList->RemoveItem(thePtr)); 165 assert(!nonCacheList->HasItem(thePtr)); 166 } else { 167 assert(!cacheList->HasItem(thePtr)); 168 assert(nonCacheList->RemoveItem(thePtr)); 169 } 170 if (isMallocTest) { 171 free(thePtr); 172 } else { 173 delete[] thePtr; 174 } 175 } 176 177 178 /* 179 * Method: BlockCacheConcurrencyTest::TestBlockCache() 180 * Descr: This method performs the tests on BBlockCache. It is 181 * called by 6 threads concurrently. Three of them are 182 * operating on the B_OBJECT_CACHE instance of BBlockCache 183 * and the other three are operating on the B_MALLOC_CACHE 184 * instance. 185 * 186 * The goal of this method is to perform a series of get, 187 * save and free operations on block from the cache using 188 * "cache size" and "non-cache size" blocks. Also, at the 189 * end of this method, all blocks unfreed by this method are 190 * freed to avoid a memory leak. 191 */ 192 193 194 void BlockCacheConcurrencyTest::TestBlockCache(BBlockCache *theCache, 195 bool isMallocTest) 196 { 197 BList cacheList; 198 BList nonCacheList; 199 thread_id theThread = find_thread(NULL); 200 201 // Do everything eight times to ensure the test runs long 202 // enough to check for concurrency problems. 203 for (int j = 0; j < 8; j++) { 204 // Perform a series of gets, saves and frees 205 for (int i = 0; i < numBlocksInCache / 2; i++) { 206 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList); 207 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList); 208 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList); 209 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList); 210 211 SaveBlock(theCache, cacheList.ItemAt(cacheList.CountItems() / 2), 212 sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList); 213 SaveBlock(theCache, nonCacheList.ItemAt(nonCacheList.CountItems() / 2), 214 sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList); 215 216 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList); 217 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList); 218 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList); 219 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList); 220 221 FreeBlock(cacheList.ItemAt(cacheList.CountItems() / 2), 222 sizeOfBlocksInCache, isMallocTest, theThread, &cacheList, &nonCacheList); 223 FreeBlock(nonCacheList.ItemAt(nonCacheList.CountItems() / 2), 224 sizeOfNonCacheBlocks, isMallocTest, theThread, &cacheList, &nonCacheList); 225 } 226 bool performFree = false; 227 // Free or save (every other block) for all "cache sized" blocks. 228 while (!cacheList.IsEmpty()) { 229 if (performFree) { 230 FreeBlock(cacheList.LastItem(), sizeOfBlocksInCache, isMallocTest, theThread, &cacheList, 231 &nonCacheList); 232 } else { 233 SaveBlock(theCache, cacheList.LastItem(), sizeOfBlocksInCache, theThread, &cacheList, 234 &nonCacheList); 235 } 236 performFree = !performFree; 237 } 238 // Free or save (every other block) for all "non-cache sized" blocks. 239 while (!nonCacheList.IsEmpty()) { 240 if (performFree) { 241 FreeBlock(nonCacheList.LastItem(), sizeOfNonCacheBlocks, isMallocTest, theThread, &cacheList, 242 &nonCacheList); 243 } else { 244 SaveBlock(theCache, nonCacheList.LastItem(), sizeOfNonCacheBlocks, theThread, &cacheList, 245 &nonCacheList); 246 } 247 performFree = !performFree; 248 } 249 } 250 } 251 252 253 /* 254 * Method: BlockCacheConcurrencyTest::TestThreadMalloc() 255 * Descr: This method passes the BBlockCache instance to TestBlockCache() 256 * where the instance will be tested. 257 */ 258 259 260 void BlockCacheConcurrencyTest::TestThreadMalloc(void) 261 { 262 TestBlockCache(theMallocCache, true); 263 } 264 265 266 /* 267 * Method: BlockCacheConcurrencyTest::TestThreadObj() 268 * Descr: This method passes the BBlockCache instance to TestBlockCache() 269 * where the instance will be tested. 270 */ 271 272 273 void BlockCacheConcurrencyTest::TestThreadObj(void) 274 { 275 TestBlockCache(theObjCache, false); 276 } 277 278 279 /* 280 * Method: BlockCacheConcurrencyTest::suite() 281 * Descr: This static member function returns a test caller for performing 282 * the "BlockCacheConcurrencyTest" test. The test caller 283 * is created as a ThreadedTestCaller with six independent threads. 284 */ 285 286 287 CppUnit::Test *BlockCacheConcurrencyTest::suite(void) 288 { 289 typedef BThreadedTestCaller <BlockCacheConcurrencyTest > 290 BlockCacheConcurrencyTestCaller; 291 292 BlockCacheConcurrencyTest *theTest = new BlockCacheConcurrencyTest(""); 293 BlockCacheConcurrencyTestCaller *threadedTest = new BlockCacheConcurrencyTestCaller("BBlockCache::Concurrency Test", theTest); 294 threadedTest->addThread("A", &BlockCacheConcurrencyTest::TestThreadObj); 295 threadedTest->addThread("B", &BlockCacheConcurrencyTest::TestThreadObj); 296 threadedTest->addThread("C", &BlockCacheConcurrencyTest::TestThreadObj); 297 threadedTest->addThread("D", &BlockCacheConcurrencyTest::TestThreadMalloc); 298 threadedTest->addThread("E", &BlockCacheConcurrencyTest::TestThreadMalloc); 299 threadedTest->addThread("F", &BlockCacheConcurrencyTest::TestThreadMalloc); 300 return(threadedTest); 301 } 302