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