deThreadTest.c revision 3c827367444ee418f129b2c238299f49d3264554
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
36static void threadTestThr1 (void* arg)
37{
38	deInt32 val = *((deInt32*)arg);
39	DE_TEST_ASSERT(val == 123);
40}
41
42static void threadTestThr2 (void* arg)
43{
44	DE_UNREF(arg);
45	deSleep(100);
46}
47
48typedef struct ThreadData3_s
49{
50	deUint8		bytes[16];
51} ThreadData3;
52
53static 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
65static 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
73static DE_THREAD_LOCAL int tls_testVar = 123;
74
75static 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
85void 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
174static void mutexTestThr1 (void* arg)
175{
176	deMutex		mutex	= *((deMutex*)arg);
177
178	deMutex_lock(mutex);
179	deMutex_unlock(mutex);
180}
181
182typedef struct MutexData2_s
183{
184	deMutex		mutex;
185	deInt32		counter;
186	deInt32		counter2;
187	deInt32		maxVal;
188} MutexData2;
189
190static 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
223void 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
232void 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
356typedef struct TestBuffer_s
357{
358	deInt32			buffer[32];
359	deSemaphore		empty;
360	deSemaphore		fill;
361
362	deInt32			producerSum;
363	deInt32			consumerSum;
364} TestBuffer;
365
366void 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		deInt32 val;
379
380		if (ndx == numToProduce)
381		{
382			val = 0; /* End. */
383		}
384		else
385		{
386			val = (deInt32)deRandom_getUint32(&random);
387			val = val ? val : 1;
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->producerSum += val;
398	}
399}
400
401void 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->consumerSum += val;
418
419		if (val == 0)
420			break;
421	}
422}
423
424void 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		DE_TEST_ASSERT(testBuffer.producerSum == testBuffer.consumerSum);
465	}
466}
467
468void deAtomic_selfTest (void)
469{
470	/* Single-threaded tests. */
471	{
472		volatile int a = 11;
473		DE_TEST_ASSERT(deAtomicIncrement32(&a) == 12);
474		DE_TEST_ASSERT(a == 12);
475		DE_TEST_ASSERT(deAtomicIncrement32(&a) == 13);
476		DE_TEST_ASSERT(a == 13);
477
478		DE_TEST_ASSERT(deAtomicDecrement32(&a) == 12);
479		DE_TEST_ASSERT(a == 12);
480		DE_TEST_ASSERT(deAtomicDecrement32(&a) == 11);
481		DE_TEST_ASSERT(a == 11);
482	}
483
484	{
485		volatile deUint32 p;
486
487		p = 0;
488		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 0, 1) == 0);
489		DE_TEST_ASSERT(p == 1);
490
491		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 0, 2) == 1);
492		DE_TEST_ASSERT(p == 1);
493
494		p = 7;
495		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 6, 8) == 7);
496		DE_TEST_ASSERT(p == 7);
497
498		DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 7, 8) == 7);
499		DE_TEST_ASSERT(p == 8);
500	}
501
502	/* \todo [2012-10-26 pyry] Implement multi-threaded tests. */
503}
504
505/* Singleton self-test. */
506
507DE_DECLARE_POOL_ARRAY(deThreadArray, deThread);
508
509static volatile	deSingletonState	s_testSingleton				= DE_SINGLETON_STATE_NOT_INITIALIZED;
510static volatile int					s_testSingletonInitCount	= 0;
511static deBool						s_testSingletonInitialized	= DE_FALSE;
512static volatile deBool				s_singletonInitLock			= DE_FALSE;
513
514static void waitForSingletonInitLock (void)
515{
516	for (;;)
517	{
518		deMemoryReadWriteFence();
519
520		if (s_singletonInitLock)
521			break;
522	}
523}
524
525static void initTestSingleton (void* arg)
526{
527	int initTimeMs = *(const int*)arg;
528
529	if (initTimeMs >= 0)
530		deSleep((deUint32)initTimeMs);
531
532	deAtomicIncrement32(&s_testSingletonInitCount);
533	s_testSingletonInitialized = DE_TRUE;
534}
535
536static void singletonTestThread (void* arg)
537{
538	waitForSingletonInitLock();
539
540	deInitSingleton(&s_testSingleton, initTestSingleton, arg);
541	DE_TEST_ASSERT(s_testSingletonInitialized);
542}
543
544static void resetTestState (void)
545{
546	s_testSingleton				= DE_SINGLETON_STATE_NOT_INITIALIZED;
547	s_testSingletonInitCount	= 0;
548	s_testSingletonInitialized	= DE_FALSE;
549	s_singletonInitLock			= DE_FALSE;
550}
551
552static void runSingletonThreadedTest (int numThreads, int initTimeMs)
553{
554	deMemPool*		tmpPool		= deMemPool_createRoot(DE_NULL, 0);
555	deThreadArray*	threads		= tmpPool ? deThreadArray_create(tmpPool) : DE_NULL;
556	int				threadNdx;
557
558	resetTestState();
559
560	for (threadNdx = 0; threadNdx < numThreads; threadNdx++)
561	{
562		deThread thread = deThread_create(singletonTestThread, &initTimeMs, DE_NULL);
563		DE_TEST_ASSERT(thread);
564		DE_TEST_ASSERT(deThreadArray_pushBack(threads, thread));
565	}
566
567	/* All threads created - let them do initialization. */
568	deMemoryReadWriteFence();
569	s_singletonInitLock = DE_TRUE;
570	deMemoryReadWriteFence();
571
572	for (threadNdx = 0; threadNdx < numThreads; threadNdx++)
573	{
574		deThread thread = deThreadArray_get(threads, threadNdx);
575		DE_TEST_ASSERT(deThread_join(thread));
576		deThread_destroy(thread);
577	}
578
579	/* Verify results. */
580	DE_TEST_ASSERT(s_testSingletonInitialized);
581	DE_TEST_ASSERT(s_testSingletonInitCount == 1);
582
583	deMemPool_destroy(tmpPool);
584}
585
586void deSingleton_selfTest (void)
587{
588	const struct
589	{
590		int		numThreads;
591		int		initTimeMs;
592		int		repeatCount;
593	} cases[] =
594	{
595	/*	#threads	time	#repeat	*/
596		{ 1,		-1,		5	},
597		{ 1,		1,		5	},
598		{ 2,		-1,		20	},
599		{ 2,		1,		20	},
600		{ 4,		-1,		20	},
601		{ 4,		1,		20	},
602		{ 4,		5,		20	}
603	};
604	int caseNdx;
605
606	for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
607	{
608		int		numThreads		= cases[caseNdx].numThreads;
609		int		initTimeMs		= cases[caseNdx].initTimeMs;
610		int		repeatCount		= cases[caseNdx].repeatCount;
611		int		subCaseNdx;
612
613		for (subCaseNdx = 0; subCaseNdx < repeatCount; subCaseNdx++)
614			runSingletonThreadedTest(numThreads, initTimeMs);
615	}
616}
617