/*------------------------------------------------------------------------- * drawElements Thread Library * --------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Thread library tests. *//*--------------------------------------------------------------------*/ #include "deThreadTest.h" #include "deThread.h" #include "deMutex.h" #include "deSemaphore.h" #include "deMemory.h" #include "deRandom.h" #include "deAtomic.h" #include "deThreadLocal.h" #include "deSingleton.h" #include "deMemPool.h" #include "dePoolArray.h" static void threadTestThr1 (void* arg) { deInt32 val = *((deInt32*)arg); DE_TEST_ASSERT(val == 123); } static void threadTestThr2 (void* arg) { DE_UNREF(arg); deSleep(100); } typedef struct ThreadData3_s { deUint8 bytes[16]; } ThreadData3; static void threadTestThr3 (void* arg) { ThreadData3* data = (ThreadData3*)arg; int ndx; for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(data->bytes); ndx++) DE_TEST_ASSERT(data->bytes[ndx] == 0); for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(data->bytes); ndx++) data->bytes[ndx] = 0xff; } static void threadTestThr4 (void* arg) { deThreadLocal tls = *(deThreadLocal*)arg; deThreadLocal_set(tls, DE_NULL); } #if defined(DE_THREAD_LOCAL) static DE_THREAD_LOCAL int tls_testVar = 123; static void tlsTestThr (void* arg) { DE_UNREF(arg); DE_TEST_ASSERT(tls_testVar == 123); tls_testVar = 104; DE_TEST_ASSERT(tls_testVar == 104); } #endif void deThread_selfTest (void) { /* Test sleep & yield. */ deSleep(0); deSleep(100); deYield(); /* Thread test 1. */ { deInt32 val = 123; deBool ret; deThread thread = deThread_create(threadTestThr1, &val, DE_NULL); DE_TEST_ASSERT(thread); ret = deThread_join(thread); DE_TEST_ASSERT(ret); deThread_destroy(thread); } /* Thread test 2. */ { deThread thread = deThread_create(threadTestThr2, DE_NULL, DE_NULL); deInt32 ret; DE_TEST_ASSERT(thread); ret = deThread_join(thread); DE_TEST_ASSERT(ret); deThread_destroy(thread); } /* Thread test 3. */ { ThreadData3 data; deThread thread; deBool ret; int ndx; deMemset(&data, 0, sizeof(ThreadData3)); thread = deThread_create(threadTestThr3, &data, DE_NULL); DE_TEST_ASSERT(thread); ret = deThread_join(thread); DE_TEST_ASSERT(ret); for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(data.bytes); ndx++) DE_TEST_ASSERT(data.bytes[ndx] == 0xff); deThread_destroy(thread); } /* Test tls. */ { deThreadLocal tls; deThread thread; tls = deThreadLocal_create(); DE_TEST_ASSERT(tls); deThreadLocal_set(tls, (void*)(deUintptr)0xff); thread = deThread_create(threadTestThr4, &tls, DE_NULL); deThread_join(thread); deThread_destroy(thread); DE_TEST_ASSERT((deUintptr)deThreadLocal_get(tls) == 0xff); deThreadLocal_destroy(tls); } #if defined(DE_THREAD_LOCAL) { deThread thread; DE_TEST_ASSERT(tls_testVar == 123); tls_testVar = 1; DE_TEST_ASSERT(tls_testVar == 1); thread = deThread_create(tlsTestThr, DE_NULL, DE_NULL); deThread_join(thread); deThread_destroy(thread); DE_TEST_ASSERT(tls_testVar == 1); tls_testVar = 123; } #endif } static void mutexTestThr1 (void* arg) { deMutex mutex = *((deMutex*)arg); deMutex_lock(mutex); deMutex_unlock(mutex); } typedef struct MutexData2_s { deMutex mutex; deInt32 counter; deInt32 counter2; deInt32 maxVal; } MutexData2; static void mutexTestThr2 (void* arg) { MutexData2* data = (MutexData2*)arg; deInt32 numIncremented = 0; for (;;) { deInt32 localCounter; deMutex_lock(data->mutex); if (data->counter >= data->maxVal) { deMutex_unlock(data->mutex); break; } localCounter = data->counter; deYield(); DE_TEST_ASSERT(localCounter == data->counter); localCounter += 1; data->counter = localCounter; deMutex_unlock(data->mutex); numIncremented++; } deMutex_lock(data->mutex); data->counter2 += numIncremented; deMutex_unlock(data->mutex); } void mutexTestThr3 (void* arg) { deMutex mutex = *((deMutex*)arg); deBool ret; ret = deMutex_tryLock(mutex); DE_TEST_ASSERT(!ret); } void deMutex_selfTest (void) { /* Default mutex from single thread. */ { deMutex mutex = deMutex_create(DE_NULL); deBool ret; DE_TEST_ASSERT(mutex); deMutex_lock(mutex); deMutex_unlock(mutex); /* Should succeed. */ ret = deMutex_tryLock(mutex); DE_TEST_ASSERT(ret); deMutex_unlock(mutex); deMutex_destroy(mutex); } /* Recursive mutex. */ { deMutexAttributes attrs; deMutex mutex; int ndx; int numLocks = 10; deMemset(&attrs, 0, sizeof(attrs)); attrs.flags = DE_MUTEX_RECURSIVE; mutex = deMutex_create(&attrs); DE_TEST_ASSERT(mutex); for (ndx = 0; ndx < numLocks; ndx++) deMutex_lock(mutex); for (ndx = 0; ndx < numLocks; ndx++) deMutex_unlock(mutex); deMutex_destroy(mutex); } /* Mutex and threads. */ { deMutex mutex; deThread thread; mutex = deMutex_create(DE_NULL); DE_TEST_ASSERT(mutex); deMutex_lock(mutex); thread = deThread_create(mutexTestThr1, &mutex, DE_NULL); DE_TEST_ASSERT(thread); deSleep(100); deMutex_unlock(mutex); deMutex_lock(mutex); deMutex_unlock(mutex); deThread_join(thread); deThread_destroy(thread); deMutex_destroy(mutex); } /* A bit more complex mutex test. */ { MutexData2 data; deThread threads[2]; int ndx; data.mutex = deMutex_create(DE_NULL); DE_TEST_ASSERT(data.mutex); data.counter = 0; data.counter2 = 0; data.maxVal = 1000; deMutex_lock(data.mutex); for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(threads); ndx++) { threads[ndx] = deThread_create(mutexTestThr2, &data, DE_NULL); DE_TEST_ASSERT(threads[ndx]); } deMutex_unlock(data.mutex); for (ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(threads); ndx++) { deBool ret = deThread_join(threads[ndx]); DE_TEST_ASSERT(ret); deThread_destroy(threads[ndx]); } DE_TEST_ASSERT(data.counter == data.counter2); DE_TEST_ASSERT(data.maxVal == data.counter); deMutex_destroy(data.mutex); } /* tryLock() deadlock test. */ { deThread thread; deMutex mutex = deMutex_create(DE_NULL); deBool ret; DE_TEST_ASSERT(mutex); deMutex_lock(mutex); thread = deThread_create(mutexTestThr3, &mutex, DE_NULL); DE_TEST_ASSERT(mutex); ret = deThread_join(thread); DE_TEST_ASSERT(ret); deMutex_unlock(mutex); deMutex_destroy(mutex); deThread_destroy(thread); } } typedef struct TestBuffer_s { deInt32 buffer[32]; deSemaphore empty; deSemaphore fill; deInt32 producerSum; deInt32 consumerSum; } TestBuffer; void producerThread (void* arg) { TestBuffer* buffer = (TestBuffer*)arg; deRandom random; int ndx; int numToProduce = 10000; int writePos = 0; deRandom_init(&random, 123); for (ndx = 0; ndx <= numToProduce; ndx++) { deInt32 val; if (ndx == numToProduce) { val = 0; /* End. */ } else { val = (deInt32)deRandom_getUint32(&random); val = val ? val : 1; } deSemaphore_decrement(buffer->empty); buffer->buffer[writePos] = val; writePos = (writePos + 1) % DE_LENGTH_OF_ARRAY(buffer->buffer); deSemaphore_increment(buffer->fill); buffer->producerSum += val; } } void consumerThread (void* arg) { TestBuffer* buffer = (TestBuffer*)arg; int readPos = 0; for (;;) { deInt32 val; deSemaphore_decrement(buffer->fill); val = buffer->buffer[readPos]; readPos = (readPos + 1) % DE_LENGTH_OF_ARRAY(buffer->buffer); deSemaphore_increment(buffer->empty); buffer->consumerSum += val; if (val == 0) break; } } void deSemaphore_selfTest (void) { /* Basic test. */ { deSemaphore semaphore = deSemaphore_create(1, DE_NULL); DE_TEST_ASSERT(semaphore); deSemaphore_increment(semaphore); deSemaphore_decrement(semaphore); deSemaphore_decrement(semaphore); deSemaphore_destroy(semaphore); } /* Producer-consumer test. */ { TestBuffer testBuffer; deThread producer; deThread consumer; deBool ret; deMemset(&testBuffer, 0, sizeof(testBuffer)); testBuffer.empty = deSemaphore_create(DE_LENGTH_OF_ARRAY(testBuffer.buffer), DE_NULL); testBuffer.fill = deSemaphore_create(0, DE_NULL); DE_TEST_ASSERT(testBuffer.empty && testBuffer.fill); consumer = deThread_create(consumerThread, &testBuffer, DE_NULL); producer = deThread_create(producerThread, &testBuffer, DE_NULL); DE_TEST_ASSERT(consumer && producer); ret = deThread_join(consumer) && deThread_join(producer); DE_TEST_ASSERT(ret); deThread_destroy(producer); deThread_destroy(consumer); DE_TEST_ASSERT(testBuffer.producerSum == testBuffer.consumerSum); } } void deAtomic_selfTest (void) { /* Single-threaded tests. */ { volatile int a = 11; DE_TEST_ASSERT(deAtomicIncrement32(&a) == 12); DE_TEST_ASSERT(a == 12); DE_TEST_ASSERT(deAtomicIncrement32(&a) == 13); DE_TEST_ASSERT(a == 13); DE_TEST_ASSERT(deAtomicDecrement32(&a) == 12); DE_TEST_ASSERT(a == 12); DE_TEST_ASSERT(deAtomicDecrement32(&a) == 11); DE_TEST_ASSERT(a == 11); } { volatile deUint32 p; p = 0; DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 0, 1) == 0); DE_TEST_ASSERT(p == 1); DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 0, 2) == 1); DE_TEST_ASSERT(p == 1); p = 7; DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 6, 8) == 7); DE_TEST_ASSERT(p == 7); DE_TEST_ASSERT(deAtomicCompareExchange32(&p, 7, 8) == 7); DE_TEST_ASSERT(p == 8); } /* \todo [2012-10-26 pyry] Implement multi-threaded tests. */ } /* Singleton self-test. */ DE_DECLARE_POOL_ARRAY(deThreadArray, deThread); static volatile deSingletonState s_testSingleton = DE_SINGLETON_STATE_NOT_INITIALIZED; static volatile int s_testSingletonInitCount = 0; static deBool s_testSingletonInitialized = DE_FALSE; static volatile deBool s_singletonInitLock = DE_FALSE; static void waitForSingletonInitLock (void) { for (;;) { deMemoryReadWriteFence(); if (s_singletonInitLock) break; } } static void initTestSingleton (void* arg) { int initTimeMs = *(const int*)arg; if (initTimeMs >= 0) deSleep((deUint32)initTimeMs); deAtomicIncrement32(&s_testSingletonInitCount); s_testSingletonInitialized = DE_TRUE; } static void singletonTestThread (void* arg) { waitForSingletonInitLock(); deInitSingleton(&s_testSingleton, initTestSingleton, arg); DE_TEST_ASSERT(s_testSingletonInitialized); } static void resetTestState (void) { s_testSingleton = DE_SINGLETON_STATE_NOT_INITIALIZED; s_testSingletonInitCount = 0; s_testSingletonInitialized = DE_FALSE; s_singletonInitLock = DE_FALSE; } static void runSingletonThreadedTest (int numThreads, int initTimeMs) { deMemPool* tmpPool = deMemPool_createRoot(DE_NULL, 0); deThreadArray* threads = tmpPool ? deThreadArray_create(tmpPool) : DE_NULL; int threadNdx; resetTestState(); for (threadNdx = 0; threadNdx < numThreads; threadNdx++) { deThread thread = deThread_create(singletonTestThread, &initTimeMs, DE_NULL); DE_TEST_ASSERT(thread); DE_TEST_ASSERT(deThreadArray_pushBack(threads, thread)); } /* All threads created - let them do initialization. */ deMemoryReadWriteFence(); s_singletonInitLock = DE_TRUE; deMemoryReadWriteFence(); for (threadNdx = 0; threadNdx < numThreads; threadNdx++) { deThread thread = deThreadArray_get(threads, threadNdx); DE_TEST_ASSERT(deThread_join(thread)); deThread_destroy(thread); } /* Verify results. */ DE_TEST_ASSERT(s_testSingletonInitialized); DE_TEST_ASSERT(s_testSingletonInitCount == 1); deMemPool_destroy(tmpPool); } void deSingleton_selfTest (void) { const struct { int numThreads; int initTimeMs; int repeatCount; } cases[] = { /* #threads time #repeat */ { 1, -1, 5 }, { 1, 1, 5 }, { 2, -1, 20 }, { 2, 1, 20 }, { 4, -1, 20 }, { 4, 1, 20 }, { 4, 5, 20 } }; int caseNdx; for (caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++) { int numThreads = cases[caseNdx].numThreads; int initTimeMs = cases[caseNdx].initTimeMs; int repeatCount = cases[caseNdx].repeatCount; int subCaseNdx; for (subCaseNdx = 0; subCaseNdx < repeatCount; subCaseNdx++) runSingletonThreadedTest(numThreads, initTimeMs); } }