1 /*-------------------------------------------------------------------------
2  * drawElements Thread Library
3  * ---------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Thread library tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "deThreadTest.h"
25 #include "deThread.h"
26 #include "deMutex.h"
27 #include "deSemaphore.h"
28 #include "deMemory.h"
29 #include "deRandom.h"
30 #include "deAtomic.h"
31 #include "deThreadLocal.h"
32 #include "deSingleton.h"
33 #include "deMemPool.h"
34 #include "dePoolArray.h"
35 
threadTestThr1(void * arg)36 static void threadTestThr1 (void* arg)
37 {
38 	deInt32 val = *((deInt32*)arg);
39 	DE_TEST_ASSERT(val == 123);
40 }
41 
threadTestThr2(void * arg)42 static void threadTestThr2 (void* arg)
43 {
44 	DE_UNREF(arg);
45 	deSleep(100);
46 }
47 
48 typedef struct ThreadData3_s
49 {
50 	deUint8		bytes[16];
51 } ThreadData3;
52 
threadTestThr3(void * arg)53 static void threadTestThr3 (void* arg)
54 {
55 	ThreadData3* data = (ThreadData3*)arg;
56 	int ndx;
57 
58 	for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(data->bytes); ndx++)
59 		DE_TEST_ASSERT(data->bytes[ndx] == 0);
60 
61 	for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(data->bytes); ndx++)
62 		data->bytes[ndx] = 0xff;
63 }
64 
threadTestThr4(void * arg)65 static void threadTestThr4 (void* arg)
66 {
67 	deThreadLocal tls = *(deThreadLocal*)arg;
68 	deThreadLocal_set(tls, DE_NULL);
69 }
70 
71 #if defined(DE_THREAD_LOCAL)
72 
73 static DE_THREAD_LOCAL int tls_testVar = 123;
74 
tlsTestThr(void * arg)75 static void tlsTestThr (void* arg)
76 {
77 	DE_UNREF(arg);
78 	DE_TEST_ASSERT(tls_testVar == 123);
79 	tls_testVar = 104;
80 	DE_TEST_ASSERT(tls_testVar == 104);
81 }
82 
83 #endif
84 
deThread_selfTest(void)85 void deThread_selfTest (void)
86 {
87 	/* Test sleep & yield. */
88 	deSleep(0);
89 	deSleep(100);
90 	deYield();
91 
92 	/* Thread test 1. */
93 	{
94 		deInt32		val		= 123;
95 		deBool		ret;
96 		deThread	thread	= deThread_create(threadTestThr1, &val, DE_NULL);
97 		DE_TEST_ASSERT(thread);
98 
99 		ret = deThread_join(thread);
100 		DE_TEST_ASSERT(ret);
101 
102 		deThread_destroy(thread);
103 	}
104 
105 	/* Thread test 2. */
106 	{
107 		deThread	thread	= deThread_create(threadTestThr2, DE_NULL, DE_NULL);
108 		deInt32		ret;
109 		DE_TEST_ASSERT(thread);
110 
111 		ret = deThread_join(thread);
112 		DE_TEST_ASSERT(ret);
113 
114 		deThread_destroy(thread);
115 	}
116 
117 	/* Thread test 3. */
118 	{
119 		ThreadData3	data;
120 		deThread	thread;
121 		deBool		ret;
122 		int			ndx;
123 
124 		deMemset(&data, 0, sizeof(ThreadData3));
125 
126 		thread = deThread_create(threadTestThr3, &data, DE_NULL);
127 		DE_TEST_ASSERT(thread);
128 
129 		ret = deThread_join(thread);
130 		DE_TEST_ASSERT(ret);
131 
132 		for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(data.bytes); ndx++)
133 			DE_TEST_ASSERT(data.bytes[ndx] == 0xff);
134 
135 		deThread_destroy(thread);
136 	}
137 
138 	/* Test tls. */
139 	{
140 		deThreadLocal	tls;
141 		deThread		thread;
142 
143 		tls = deThreadLocal_create();
144 		DE_TEST_ASSERT(tls);
145 
146 		deThreadLocal_set(tls, (void*)(deUintptr)0xff);
147 
148 		thread = deThread_create(threadTestThr4, &tls, DE_NULL);
149 		deThread_join(thread);
150 		deThread_destroy(thread);
151 
152 		DE_TEST_ASSERT((deUintptr)deThreadLocal_get(tls) == 0xff);
153 		deThreadLocal_destroy(tls);
154 	}
155 
156 #if defined(DE_THREAD_LOCAL)
157 	{
158 		deThread thread;
159 
160 		DE_TEST_ASSERT(tls_testVar == 123);
161 		tls_testVar = 1;
162 		DE_TEST_ASSERT(tls_testVar == 1);
163 
164 		thread = deThread_create(tlsTestThr, DE_NULL, DE_NULL);
165 		deThread_join(thread);
166 		deThread_destroy(thread);
167 
168 		DE_TEST_ASSERT(tls_testVar == 1);
169 		tls_testVar = 123;
170 	}
171 #endif
172 }
173 
mutexTestThr1(void * arg)174 static void mutexTestThr1 (void* arg)
175 {
176 	deMutex		mutex	= *((deMutex*)arg);
177 
178 	deMutex_lock(mutex);
179 	deMutex_unlock(mutex);
180 }
181 
182 typedef struct MutexData2_s
183 {
184 	deMutex		mutex;
185 	deInt32		counter;
186 	deInt32		counter2;
187 	deInt32		maxVal;
188 } MutexData2;
189 
mutexTestThr2(void * arg)190 static void mutexTestThr2 (void* arg)
191 {
192 	MutexData2* data = (MutexData2*)arg;
193 	deInt32 numIncremented = 0;
194 
195 	for (;;)
196 	{
197 		deInt32 localCounter;
198 		deMutex_lock(data->mutex);
199 
200 		if (data->counter >= data->maxVal)
201 		{
202 			deMutex_unlock(data->mutex);
203 			break;
204 		}
205 
206 		localCounter = data->counter;
207 		deYield();
208 
209 		DE_TEST_ASSERT(localCounter == data->counter);
210 		localCounter += 1;
211 		data->counter = localCounter;
212 
213 		deMutex_unlock(data->mutex);
214 
215 		numIncremented++;
216 	}
217 
218 	deMutex_lock(data->mutex);
219 	data->counter2 += numIncremented;
220 	deMutex_unlock(data->mutex);
221 }
222 
mutexTestThr3(void * arg)223 void mutexTestThr3 (void* arg)
224 {
225 	deMutex mutex = *((deMutex*)arg);
226 	deBool	ret;
227 
228 	ret = deMutex_tryLock(mutex);
229 	DE_TEST_ASSERT(!ret);
230 }
231 
deMutex_selfTest(void)232 void deMutex_selfTest (void)
233 {
234 	/* Default mutex from single thread. */
235 	{
236 		deMutex mutex = deMutex_create(DE_NULL);
237 		deBool	ret;
238 		DE_TEST_ASSERT(mutex);
239 
240 		deMutex_lock(mutex);
241 		deMutex_unlock(mutex);
242 
243 		/* Should succeed. */
244 		ret = deMutex_tryLock(mutex);
245 		DE_TEST_ASSERT(ret);
246 		deMutex_unlock(mutex);
247 
248 		deMutex_destroy(mutex);
249 	}
250 
251 	/* Recursive mutex. */
252 	{
253 		deMutexAttributes	attrs;
254 		deMutex				mutex;
255 		int					ndx;
256 		int					numLocks	= 10;
257 
258 		deMemset(&attrs, 0, sizeof(attrs));
259 
260 		attrs.flags = DE_MUTEX_RECURSIVE;
261 
262 		mutex = deMutex_create(&attrs);
263 		DE_TEST_ASSERT(mutex);
264 
265 		for (ndx = 0; ndx < numLocks; ndx++)
266 			deMutex_lock(mutex);
267 
268 		for (ndx = 0; ndx < numLocks; ndx++)
269 			deMutex_unlock(mutex);
270 
271 		deMutex_destroy(mutex);
272 	}
273 
274 	/* Mutex and threads. */
275 	{
276 		deMutex		mutex;
277 		deThread	thread;
278 
279 		mutex = deMutex_create(DE_NULL);
280 		DE_TEST_ASSERT(mutex);
281 
282 		deMutex_lock(mutex);
283 
284 		thread = deThread_create(mutexTestThr1, &mutex, DE_NULL);
285 		DE_TEST_ASSERT(thread);
286 
287 		deSleep(100);
288 		deMutex_unlock(mutex);
289 
290 		deMutex_lock(mutex);
291 		deMutex_unlock(mutex);
292 
293 		deThread_join(thread);
294 
295 		deThread_destroy(thread);
296 		deMutex_destroy(mutex);
297 	}
298 
299 	/* A bit more complex mutex test. */
300 	{
301 		MutexData2	data;
302 		deThread	threads[2];
303 		int			ndx;
304 
305 		data.mutex	= deMutex_create(DE_NULL);
306 		DE_TEST_ASSERT(data.mutex);
307 
308 		data.counter	= 0;
309 		data.counter2	= 0;
310 		data.maxVal		= 1000;
311 
312 		deMutex_lock(data.mutex);
313 
314 		for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(threads); ndx++)
315 		{
316 			threads[ndx] = deThread_create(mutexTestThr2, &data, DE_NULL);
317 			DE_TEST_ASSERT(threads[ndx]);
318 		}
319 
320 		deMutex_unlock(data.mutex);
321 
322 		for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(threads); ndx++)
323 		{
324 			deBool ret = deThread_join(threads[ndx]);
325 			DE_TEST_ASSERT(ret);
326 			deThread_destroy(threads[ndx]);
327 		}
328 
329 		DE_TEST_ASSERT(data.counter == data.counter2);
330 		DE_TEST_ASSERT(data.maxVal == data.counter);
331 
332 		deMutex_destroy(data.mutex);
333 	}
334 
335 	/* tryLock() deadlock test. */
336 	{
337 		deThread	thread;
338 		deMutex		mutex	= deMutex_create(DE_NULL);
339 		deBool		ret;
340 		DE_TEST_ASSERT(mutex);
341 
342 		deMutex_lock(mutex);
343 
344 		thread = deThread_create(mutexTestThr3, &mutex, DE_NULL);
345 		DE_TEST_ASSERT(mutex);
346 
347 		ret = deThread_join(thread);
348 		DE_TEST_ASSERT(ret);
349 
350 		deMutex_unlock(mutex);
351 		deMutex_destroy(mutex);
352 		deThread_destroy(thread);
353 	}
354 }
355 
356 typedef struct TestBuffer_s
357 {
358 	deUint32		buffer[32];
359 	deSemaphore		empty;
360 	deSemaphore		fill;
361 
362 	deUint32		producerHash;
363 	deUint32		consumerHash;
364 } TestBuffer;
365 
producerThread(void * arg)366 void producerThread (void* arg)
367 {
368 	TestBuffer* buffer = (TestBuffer*)arg;
369 	deRandom	random;
370 	int			ndx;
371 	int			numToProduce	= 10000;
372 	int			writePos		= 0;
373 
374 	deRandom_init(&random, 123);
375 
376 	for (ndx = 0; ndx <= numToProduce; ndx++)
377 	{
378 		deUint32 val;
379 
380 		if (ndx == numToProduce)
381 		{
382 			val = 0u; /* End. */
383 		}
384 		else
385 		{
386 			val = deRandom_getUint32(&random);
387 			val = val ? val : 1u;
388 		}
389 
390 		deSemaphore_decrement(buffer->empty);
391 
392 		buffer->buffer[writePos] = val;
393 		writePos = (writePos + 1) % DE_LENGTH_OF_ARRAY(buffer->buffer);
394 
395 		deSemaphore_increment(buffer->fill);
396 
397 		buffer->producerHash ^= val;
398 	}
399 }
400 
consumerThread(void * arg)401 void consumerThread (void* arg)
402 {
403 	TestBuffer*	buffer	= (TestBuffer*)arg;
404 	int			readPos	= 0;
405 
406 	for (;;)
407 	{
408 		deInt32 val;
409 
410 		deSemaphore_decrement(buffer->fill);
411 
412 		val = buffer->buffer[readPos];
413 		readPos = (readPos + 1) % DE_LENGTH_OF_ARRAY(buffer->buffer);
414 
415 		deSemaphore_increment(buffer->empty);
416 
417 		buffer->consumerHash ^= val;
418 
419 		if (val == 0)
420 			break;
421 	}
422 }
423 
deSemaphore_selfTest(void)424 void deSemaphore_selfTest (void)
425 {
426 	/* Basic test. */
427 	{
428 		deSemaphore	semaphore	= deSemaphore_create(1, DE_NULL);
429 		DE_TEST_ASSERT(semaphore);
430 
431 		deSemaphore_increment(semaphore);
432 		deSemaphore_decrement(semaphore);
433 		deSemaphore_decrement(semaphore);
434 
435 		deSemaphore_destroy(semaphore);
436 	}
437 
438 	/* Producer-consumer test. */
439 	{
440 		TestBuffer	testBuffer;
441 		deThread	producer;
442 		deThread	consumer;
443 		deBool		ret;
444 
445 		deMemset(&testBuffer, 0, sizeof(testBuffer));
446 
447 		testBuffer.empty	= deSemaphore_create(DE_LENGTH_OF_ARRAY(testBuffer.buffer), DE_NULL);
448 		testBuffer.fill		= deSemaphore_create(0, DE_NULL);
449 
450 		DE_TEST_ASSERT(testBuffer.empty && testBuffer.fill);
451 
452 		consumer	= deThread_create(consumerThread, &testBuffer, DE_NULL);
453 		producer	= deThread_create(producerThread, &testBuffer, DE_NULL);
454 
455 		DE_TEST_ASSERT(consumer && producer);
456 
457 		ret = deThread_join(consumer) &&
458 			  deThread_join(producer);
459 		DE_TEST_ASSERT(ret);
460 
461 		deThread_destroy(producer);
462 		deThread_destroy(consumer);
463 
464 		deSemaphore_destroy(testBuffer.empty);
465 		deSemaphore_destroy(testBuffer.fill);
466 		DE_TEST_ASSERT(testBuffer.producerHash == testBuffer.consumerHash);
467 	}
468 }
469 
deAtomic_selfTest(void)470 void deAtomic_selfTest (void)
471 {
472 	/* Single-threaded tests. */
473 	{
474 		volatile deInt32 a = 11;
475 		DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == 12);
476 		DE_TEST_ASSERT(a == 12);
477 		DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == 13);
478 		DE_TEST_ASSERT(a == 13);
479 
480 		a = -2;
481 		DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == -1);
482 		DE_TEST_ASSERT(a == -1);
483 		DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == 0);
484 		DE_TEST_ASSERT(a == 0);
485 
486 		a = 11;
487 		DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == 10);
488 		DE_TEST_ASSERT(a == 10);
489 		DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == 9);
490 		DE_TEST_ASSERT(a == 9);
491 
492 		a = 0;
493 		DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == -1);
494 		DE_TEST_ASSERT(a == -1);
495 		DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == -2);
496 		DE_TEST_ASSERT(a == -2);
497 
498 		a = 0x7fffffff;
499 		DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == (int)0x80000000);
500 		DE_TEST_ASSERT(a == (int)0x80000000);
501 		DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == (int)0x7fffffff);
502 		DE_TEST_ASSERT(a == 0x7fffffff);
503 	}
504 
505 	{
506 		volatile deUint32 a = 11;
507 		DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 12);
508 		DE_TEST_ASSERT(a == 12);
509 		DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 13);
510 		DE_TEST_ASSERT(a == 13);
511 
512 		a = 0x7fffffff;
513 		DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 0x80000000);
514 		DE_TEST_ASSERT(a == 0x80000000);
515 		DE_TEST_ASSERT(deAtomicDecrementUint32(&a) == 0x7fffffff);
516 		DE_TEST_ASSERT(a == 0x7fffffff);
517 
518 		a = 0xfffffffe;
519 		DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 0xffffffff);
520 		DE_TEST_ASSERT(a == 0xffffffff);
521 		DE_TEST_ASSERT(deAtomicDecrementUint32(&a) == 0xfffffffe);
522 		DE_TEST_ASSERT(a == 0xfffffffe);
523 	}
524 
525 	{
526 		volatile deUint32 p;
527 
528 		p = 0;
529 		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 0, 1) == 0);
530 		DE_TEST_ASSERT(p == 1);
531 
532 		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 0, 2) == 1);
533 		DE_TEST_ASSERT(p == 1);
534 
535 		p = 7;
536 		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 6, 8) == 7);
537 		DE_TEST_ASSERT(p == 7);
538 
539 		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 7, 8) == 7);
540 		DE_TEST_ASSERT(p == 8);
541 	}
542 
543 #if (DE_PTR_SIZE == 8)
544 	{
545 		volatile deInt64 a = 11;
546 		DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == 12);
547 		DE_TEST_ASSERT(a == 12);
548 		DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == 13);
549 		DE_TEST_ASSERT(a == 13);
550 
551 		a = -2;
552 		DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == -1);
553 		DE_TEST_ASSERT(a == -1);
554 		DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == 0);
555 		DE_TEST_ASSERT(a == 0);
556 
557 		a = 11;
558 		DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == 10);
559 		DE_TEST_ASSERT(a == 10);
560 		DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == 9);
561 		DE_TEST_ASSERT(a == 9);
562 
563 		a = 0;
564 		DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == -1);
565 		DE_TEST_ASSERT(a == -1);
566 		DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == -2);
567 		DE_TEST_ASSERT(a == -2);
568 
569 		a = (deInt64)((1ull << 63) - 1ull);
570 		DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == (deInt64)(1ull << 63));
571 		DE_TEST_ASSERT(a == (deInt64)(1ull << 63));
572 		DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == (deInt64)((1ull << 63) - 1));
573 		DE_TEST_ASSERT(a == (deInt64)((1ull << 63) - 1));
574 	}
575 #endif /* (DE_PTR_SIZE == 8) */
576 
577 	/* \todo [2012-10-26 pyry] Implement multi-threaded tests. */
578 }
579 
580 /* Singleton self-test. */
581 
582 DE_DECLARE_POOL_ARRAY(deThreadArray, deThread);
583 
584 static volatile	deSingletonState	s_testSingleton				= DE_SINGLETON_STATE_NOT_INITIALIZED;
585 static volatile int					s_testSingletonInitCount	= 0;
586 static deBool						s_testSingletonInitialized	= DE_FALSE;
587 static volatile deBool				s_singletonInitLock			= DE_FALSE;
588 
waitForSingletonInitLock(void)589 static void waitForSingletonInitLock (void)
590 {
591 	for (;;)
592 	{
593 		deMemoryReadWriteFence();
594 
595 		if (s_singletonInitLock)
596 			break;
597 	}
598 }
599 
initTestSingleton(void * arg)600 static void initTestSingleton (void* arg)
601 {
602 	int initTimeMs = *(const int*)arg;
603 
604 	if (initTimeMs >= 0)
605 		deSleep((deUint32)initTimeMs);
606 
607 	deAtomicIncrement32(&s_testSingletonInitCount);
608 	s_testSingletonInitialized = DE_TRUE;
609 }
610 
singletonTestThread(void * arg)611 static void singletonTestThread (void* arg)
612 {
613 	waitForSingletonInitLock();
614 
615 	deInitSingleton(&s_testSingleton, initTestSingleton, arg);
616 	DE_TEST_ASSERT(s_testSingletonInitialized);
617 }
618 
resetTestState(void)619 static void resetTestState (void)
620 {
621 	s_testSingleton				= DE_SINGLETON_STATE_NOT_INITIALIZED;
622 	s_testSingletonInitCount	= 0;
623 	s_testSingletonInitialized	= DE_FALSE;
624 	s_singletonInitLock			= DE_FALSE;
625 }
626 
runSingletonThreadedTest(int numThreads,int initTimeMs)627 static void runSingletonThreadedTest (int numThreads, int initTimeMs)
628 {
629 	deMemPool*		tmpPool		= deMemPool_createRoot(DE_NULL, 0);
630 	deThreadArray*	threads		= tmpPool ? deThreadArray_create(tmpPool) : DE_NULL;
631 	int				threadNdx;
632 
633 	resetTestState();
634 
635 	for (threadNdx = 0; threadNdx < numThreads; threadNdx++)
636 	{
637 		deThread thread = deThread_create(singletonTestThread, &initTimeMs, DE_NULL);
638 		DE_TEST_ASSERT(thread);
639 		DE_TEST_ASSERT(deThreadArray_pushBack(threads, thread));
640 	}
641 
642 	/* All threads created - let them do initialization. */
643 	deMemoryReadWriteFence();
644 	s_singletonInitLock = DE_TRUE;
645 	deMemoryReadWriteFence();
646 
647 	for (threadNdx = 0; threadNdx < numThreads; threadNdx++)
648 	{
649 		deThread thread = deThreadArray_get(threads, threadNdx);
650 		DE_TEST_ASSERT(deThread_join(thread));
651 		deThread_destroy(thread);
652 	}
653 
654 	/* Verify results. */
655 	DE_TEST_ASSERT(s_testSingletonInitialized);
656 	DE_TEST_ASSERT(s_testSingletonInitCount == 1);
657 
658 	deMemPool_destroy(tmpPool);
659 }
660 
deSingleton_selfTest(void)661 void deSingleton_selfTest (void)
662 {
663 	const struct
664 	{
665 		int		numThreads;
666 		int		initTimeMs;
667 		int		repeatCount;
668 	} cases[] =
669 	{
670 	/*	#threads	time	#repeat	*/
671 		{ 1,		-1,		5	},
672 		{ 1,		1,		5	},
673 		{ 2,		-1,		20	},
674 		{ 2,		1,		20	},
675 		{ 4,		-1,		20	},
676 		{ 4,		1,		20	},
677 		{ 4,		5,		20	}
678 	};
679 	int caseNdx;
680 
681 	for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
682 	{
683 		int		numThreads		= cases[caseNdx].numThreads;
684 		int		initTimeMs		= cases[caseNdx].initTimeMs;
685 		int		repeatCount		= cases[caseNdx].repeatCount;
686 		int		subCaseNdx;
687 
688 		for (subCaseNdx = 0; subCaseNdx < repeatCount; subCaseNdx++)
689 			runSingletonThreadedTest(numThreads, initTimeMs);
690 	}
691 }
692