1 /*
2 This file tests BBlockCache from multiple threads to ensure there are
3 no concurrency problems.
4 */
5
6
7 #include "BlockCacheConcurrencyTest.h"
8
9 #include <stdlib.h>
10
11 #include <BlockCache.h>
12 #include <List.h>
13
14 #include "ThreadedTestCaller.h"
15
16
17 /*
18 * Method: BlockCacheConcurrencyTest::BlockCacheConcurrencyTest()
19 * Descr: This method is the only constructor for the BlockCacheConcurrencyTest
20 * class.
21 */
BlockCacheConcurrencyTest(std::string name)22 BlockCacheConcurrencyTest::BlockCacheConcurrencyTest(std::string name)
23 :
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 */
~BlockCacheConcurrencyTest()38 BlockCacheConcurrencyTest::~BlockCacheConcurrencyTest()
39 {
40 }
41
42
43 /*
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.
48 */
49 void
setUp()50 BlockCacheConcurrencyTest::setUp()
51 {
52 theObjCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache,
53 B_OBJECT_CACHE);
54 theMallocCache = new BBlockCache(numBlocksInCache, sizeOfBlocksInCache,
55 B_MALLOC_CACHE);
56 }
57
58
59 /*
60 * Method: BlockCacheConcurrencyTest::tearDown()
61 * Descr: This method cleans up the BBlockCache instances which were tested.
62 */
63 void
tearDown()64 BlockCacheConcurrencyTest::tearDown()
65 {
66 delete theObjCache;
67 delete theMallocCache;
68 }
69
70
71 /*
72 * Method: BlockCacheConcurrencyTest::GetBlock()
73 * Descr: This method returns a pointer from the BBlockCache, checking
74 * the value before passing it to the caller.
75 */
76 void *
GetBlock(BBlockCache * theCache,size_t blockSize,thread_id theThread,BList * cacheList,BList * nonCacheList)77 BlockCacheConcurrencyTest::GetBlock(BBlockCache *theCache, size_t blockSize,
78 thread_id theThread, BList *cacheList, BList *nonCacheList)
79 {
80 void *thePtr = theCache->Get(blockSize);
81
82 // The new block should not already be used by this thread.
83 CPPUNIT_ASSERT(!cacheList->HasItem(thePtr));
84 CPPUNIT_ASSERT(!nonCacheList->HasItem(thePtr));
85
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));
91 }
92
93 // Store the thread id at the start of the block for future
94 // reference.
95 *((thread_id *)thePtr) = theThread;
96 return(thePtr);
97 }
98
99
100 /*
101 * Method: BlockCacheConcurrencyTest::SavedCacheBlock()
102 * Descr: This method passes the pointer back to the BBlockCache
103 * and checks the sanity of the lists.
104 */
105 void
SaveBlock(BBlockCache * theCache,void * thePtr,size_t blockSize,thread_id theThread,BList * cacheList,BList * nonCacheList)106 BlockCacheConcurrencyTest::SaveBlock(BBlockCache *theCache, void *thePtr,
107 size_t blockSize, thread_id theThread, BList *cacheList,
108 BList *nonCacheList)
109 {
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);
115
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));
124 }
125 theCache->Save(thePtr, blockSize);
126 }
127
128
129 /*
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.
133 */
134 void
FreeBlock(void * thePtr,size_t blockSize,bool isMallocTest,thread_id theThread,BList * cacheList,BList * nonCacheList)135 BlockCacheConcurrencyTest::FreeBlock(void *thePtr, size_t blockSize,
136 bool isMallocTest, thread_id theThread, BList *cacheList,
137 BList *nonCacheList)
138 {
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);
144
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));
153 }
154 if (isMallocTest) {
155 free(thePtr);
156 } else {
157 delete[] (uint8*)thePtr;
158 }
159 }
160
161
162 /*
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.
169 *
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.
175 */
176 void
TestBlockCache(BBlockCache * theCache,bool isMallocTest)177 BlockCacheConcurrencyTest::TestBlockCache(BBlockCache *theCache,
178 bool isMallocTest)
179 {
180 BList cacheList;
181 BList nonCacheList;
182 thread_id theThread = find_thread(NULL);
183
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);
193
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);
198
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);
203
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);
208 }
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);
218 }
219 performFree = !performFree;
220 }
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);
229 }
230 performFree = !performFree;
231 }
232 }
233 }
234
235
236 /*
237 * Method: BlockCacheConcurrencyTest::TestThreadMalloc()
238 * Descr: This method passes the BBlockCache instance to TestBlockCache()
239 * where the instance will be tested.
240 */
241 void
TestThreadMalloc()242 BlockCacheConcurrencyTest::TestThreadMalloc()
243 {
244 TestBlockCache(theMallocCache, true);
245 }
246
247
248 /*
249 * Method: BlockCacheConcurrencyTest::TestThreadObj()
250 * Descr: This method passes the BBlockCache instance to TestBlockCache()
251 * where the instance will be tested.
252 */
253 void
TestThreadObj()254 BlockCacheConcurrencyTest::TestThreadObj()
255 {
256 TestBlockCache(theObjCache, false);
257 }
258
259
260 /*
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.
265 */
suite()266 CppUnit::Test *BlockCacheConcurrencyTest::suite()
267 {
268 typedef BThreadedTestCaller <BlockCacheConcurrencyTest >
269 BlockCacheConcurrencyTestCaller;
270
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);
280 }
281