xref: /haiku/src/tests/kits/support/bblockcache/BlockCacheConcurrencyTest.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
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