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