vfs: check userland buffers before reading them.
[haiku.git] / src / tests / kits / support / bblockcache / BlockCacheConcurrencyTest.cpp
blob29cd68cacbf970701cf7d04d4afea440c678f6bf
1 /*
2 This file tests BBlockCache from multiple threads to ensure there are
3 no concurrency problems.
4 */
7 #include "BlockCacheConcurrencyTest.h"
9 #include <stdlib.h>
11 #include <BlockCache.h>
12 #include <List.h>
14 #include "ThreadedTestCaller.h"
18 * Method: BlockCacheConcurrencyTest::BlockCacheConcurrencyTest()
19 * Descr: This method is the only constructor for the BlockCacheConcurrencyTest
20 * class.
22 BlockCacheConcurrencyTest::BlockCacheConcurrencyTest(std::string name)
24 BThreadedTestCase(name),
25 theObjCache(NULL),
26 theMallocCache(NULL),
27 numBlocksInCache(128),
28 sizeOfBlocksInCache(23),
29 sizeOfNonCacheBlocks(29)
35 * Method: BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest()
36 * Descr: This method is the destructor for the BlockCacheConcurrencyTest class.
38 BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest()
44 * Method: BlockCacheConcurrencyTest::setUp()
45 * Descr: This method creates a couple of BBlockCache instances to perform
46 * tests on. One uses new/delete and the other uses malloc/free
47 * on its blocks.
49 void
50 BlockCacheConcurrencyTest::setUp()
52 theObjCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache,
53 B_OBJECT_CACHE);
54 theMallocCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache,
55 B_MALLOC_CACHE);
60 * Method: BlockCacheConcurrencyTest::tearDown()
61 * Descr: This method cleans up the BBlockCache instances which were tested.
63 void
64 BlockCacheConcurrencyTest::tearDown()
66 delete theObjCache;
67 delete theMallocCache;
72 * Method: BlockCacheConcurrencyTest::GetBlock()
73 * Descr: This method returns a pointer from the BBlockCache, checking
74 * the value before passing it to the caller.
76 void *
77 BlockCacheConcurrencyTest::GetBlock(BBlockCache *theCache, size_t blockSize,
78 thread_id theThread, BList *cacheList, BList *nonCacheList)
80 void *thePtr = theCache->Get(blockSize);
82 // The new block should not already be used by this thread.
83 CPPUNIT_ASSERT(!cacheList->HasItem(thePtr));
84 CPPUNIT_ASSERT(!nonCacheList->HasItem(thePtr));
86 // Add the block to the list of blocks used by this thread.
87 if (blockSize == sizeOfBlocksInCache) {
88 CPPUNIT_ASSERT(cacheList->AddItem(thePtr));
89 } else {
90 CPPUNIT_ASSERT(nonCacheList->AddItem(thePtr));
93 // Store the thread id at the start of the block for future
94 // reference.
95 *((thread_id *)thePtr) = theThread;
96 return(thePtr);
101 * Method: BlockCacheConcurrencyTest::SavedCacheBlock()
102 * Descr: This method passes the pointer back to the BBlockCache
103 * and checks the sanity of the lists.
105 void
106 BlockCacheConcurrencyTest::SaveBlock(BBlockCache *theCache, void *thePtr,
107 size_t blockSize, thread_id theThread, BList *cacheList,
108 BList *nonCacheList)
110 // The block being returned to the cache should still have
111 // the thread id of this thread in it, or some other thread has
112 // perhaps manipulated this block which would indicate a
113 // concurrency problem.
114 CPPUNIT_ASSERT(*((thread_id *)thePtr) == theThread);
116 // Remove the item from the appropriate list and confirm it isn't
117 // on the other list for some reason.
118 if (blockSize == sizeOfBlocksInCache) {
119 CPPUNIT_ASSERT(cacheList->RemoveItem(thePtr));
120 CPPUNIT_ASSERT(!nonCacheList->HasItem(thePtr));
121 } else {
122 CPPUNIT_ASSERT(!cacheList->HasItem(thePtr));
123 CPPUNIT_ASSERT(nonCacheList->RemoveItem(thePtr));
125 theCache->Save(thePtr, blockSize);
130 * Method: BlockCacheConcurrencyTest::FreeBlock()
131 * Descr: This method frees the block directly using delete[] or free(),
132 * checking the sanity of the lists as it does the operation.
134 void
135 BlockCacheConcurrencyTest::FreeBlock(void *thePtr, size_t blockSize,
136 bool isMallocTest, thread_id theThread, BList *cacheList,
137 BList *nonCacheList)
139 // The block being returned to the cache should still have
140 // the thread id of this thread in it, or some other thread has
141 // perhaps manipulated this block which would indicate a
142 // concurrency problem.
143 CPPUNIT_ASSERT(*((thread_id *)thePtr) == theThread);
145 // Remove the item from the appropriate list and confirm it isn't
146 // on the other list for some reason.
147 if (blockSize == sizeOfBlocksInCache) {
148 CPPUNIT_ASSERT(cacheList->RemoveItem(thePtr));
149 CPPUNIT_ASSERT(!nonCacheList->HasItem(thePtr));
150 } else {
151 CPPUNIT_ASSERT(!cacheList->HasItem(thePtr));
152 CPPUNIT_ASSERT(nonCacheList->RemoveItem(thePtr));
154 if (isMallocTest) {
155 free(thePtr);
156 } else {
157 delete[] (uint8*)thePtr;
163 * Method: BlockCacheConcurrencyTest::TestBlockCache()
164 * Descr: This method performs the tests on BBlockCache. It is
165 * called by 6 threads concurrently. Three of them are
166 * operating on the B_OBJECT_CACHE instance of BBlockCache
167 * and the other three are operating on the B_MALLOC_CACHE
168 * instance.
170 * The goal of this method is to perform a series of get,
171 * save and free operations on block from the cache using
172 * "cache size" and "non-cache size" blocks. Also, at the
173 * end of this method, all blocks unfreed by this method are
174 * freed to avoid a memory leak.
176 void
177 BlockCacheConcurrencyTest::TestBlockCache(BBlockCache *theCache,
178 bool isMallocTest)
180 BList cacheList;
181 BList nonCacheList;
182 thread_id theThread = find_thread(NULL);
184 // Do everything eight times to ensure the test runs long
185 // enough to check for concurrency problems.
186 for (int j = 0; j < 8; j++) {
187 // Perform a series of gets, saves and frees
188 for (int i = 0; i < numBlocksInCache / 2; i++) {
189 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
190 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
191 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
192 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
194 SaveBlock(theCache, cacheList.ItemAt(cacheList.CountItems() / 2),
195 sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
196 SaveBlock(theCache, nonCacheList.ItemAt(nonCacheList.CountItems() / 2),
197 sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
199 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
200 GetBlock(theCache, sizeOfBlocksInCache, theThread, &cacheList, &nonCacheList);
201 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
202 GetBlock(theCache, sizeOfNonCacheBlocks, theThread, &cacheList, &nonCacheList);
204 FreeBlock(cacheList.ItemAt(cacheList.CountItems() / 2),
205 sizeOfBlocksInCache, isMallocTest, theThread, &cacheList, &nonCacheList);
206 FreeBlock(nonCacheList.ItemAt(nonCacheList.CountItems() / 2),
207 sizeOfNonCacheBlocks, isMallocTest, theThread, &cacheList, &nonCacheList);
209 bool performFree = false;
210 // Free or save (every other block) for all "cache sized" blocks.
211 while (!cacheList.IsEmpty()) {
212 if (performFree) {
213 FreeBlock(cacheList.LastItem(), sizeOfBlocksInCache, isMallocTest, theThread, &cacheList,
214 &nonCacheList);
215 } else {
216 SaveBlock(theCache, cacheList.LastItem(), sizeOfBlocksInCache, theThread, &cacheList,
217 &nonCacheList);
219 performFree = !performFree;
221 // Free or save (every other block) for all "non-cache sized" blocks.
222 while (!nonCacheList.IsEmpty()) {
223 if (performFree) {
224 FreeBlock(nonCacheList.LastItem(), sizeOfNonCacheBlocks, isMallocTest, theThread, &cacheList,
225 &nonCacheList);
226 } else {
227 SaveBlock(theCache, nonCacheList.LastItem(), sizeOfNonCacheBlocks, theThread, &cacheList,
228 &nonCacheList);
230 performFree = !performFree;
237 * Method: BlockCacheConcurrencyTest::TestThreadMalloc()
238 * Descr: This method passes the BBlockCache instance to TestBlockCache()
239 * where the instance will be tested.
241 void
242 BlockCacheConcurrencyTest::TestThreadMalloc()
244 TestBlockCache(theMallocCache, true);
249 * Method: BlockCacheConcurrencyTest::TestThreadObj()
250 * Descr: This method passes the BBlockCache instance to TestBlockCache()
251 * where the instance will be tested.
253 void
254 BlockCacheConcurrencyTest::TestThreadObj()
256 TestBlockCache(theObjCache, false);
261 * Method: BlockCacheConcurrencyTest::suite()
262 * Descr: This static member function returns a test caller for performing
263 * the "BlockCacheConcurrencyTest" test. The test caller
264 * is created as a ThreadedTestCaller with six independent threads.
266 CppUnit::Test *BlockCacheConcurrencyTest::suite()
268 typedef BThreadedTestCaller <BlockCacheConcurrencyTest >
269 BlockCacheConcurrencyTestCaller;
271 BlockCacheConcurrencyTest *theTest = new BlockCacheConcurrencyTest("");
272 BlockCacheConcurrencyTestCaller *threadedTest = new BlockCacheConcurrencyTestCaller("BBlockCache::Concurrency Test", theTest);
273 threadedTest->addThread("A", &BlockCacheConcurrencyTest::TestThreadObj);
274 threadedTest->addThread("B", &BlockCacheConcurrencyTest::TestThreadObj);
275 threadedTest->addThread("C", &BlockCacheConcurrencyTest::TestThreadObj);
276 threadedTest->addThread("D", &BlockCacheConcurrencyTest::TestThreadMalloc);
277 threadedTest->addThread("E", &BlockCacheConcurrencyTest::TestThreadMalloc);
278 threadedTest->addThread("F", &BlockCacheConcurrencyTest::TestThreadMalloc);
279 return(threadedTest);