/*------------------------------------------------------------------------- * 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) { int32_t val = *((int32_t *)arg); DE_TEST_ASSERT(val == 123); } static void threadTestThr2(void *arg) { DE_UNREF(arg); deSleep(100); } typedef struct ThreadData3_s { uint8_t 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. */ { int32_t val = 123; bool 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); int32_t ret; DE_TEST_ASSERT(thread); ret = deThread_join(thread); DE_TEST_ASSERT(ret); deThread_destroy(thread); } /* Thread test 3. */ { ThreadData3 data; deThread thread; bool 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 *)(uintptr_t)0xff); thread = deThread_create(threadTestThr4, &tls, DE_NULL); deThread_join(thread); deThread_destroy(thread); DE_TEST_ASSERT((uintptr_t)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; int32_t counter; int32_t counter2; int32_t maxVal; } MutexData2; static void mutexTestThr2(void *arg) { MutexData2 *data = (MutexData2 *)arg; int32_t numIncremented = 0; for (;;) { int32_t 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); bool ret; ret = deMutex_tryLock(mutex); DE_TEST_ASSERT(!ret); } void deMutex_selfTest(void) { /* Default mutex from single thread. */ { deMutex mutex = deMutex_create(DE_NULL); bool 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++) { bool 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); bool 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 { uint32_t buffer[32]; deSemaphore empty; deSemaphore fill; uint32_t producerHash; uint32_t consumerHash; } 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++) { uint32_t val; if (ndx == numToProduce) { val = 0u; /* End. */ } else { val = deRandom_getUint32(&random); val = val ? val : 1u; } deSemaphore_decrement(buffer->empty); buffer->buffer[writePos] = val; writePos = (writePos + 1) % DE_LENGTH_OF_ARRAY(buffer->buffer); deSemaphore_increment(buffer->fill); buffer->producerHash ^= val; } } void consumerThread(void *arg) { TestBuffer *buffer = (TestBuffer *)arg; int readPos = 0; for (;;) { int32_t val; deSemaphore_decrement(buffer->fill); val = buffer->buffer[readPos]; readPos = (readPos + 1) % DE_LENGTH_OF_ARRAY(buffer->buffer); deSemaphore_increment(buffer->empty); buffer->consumerHash ^= 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; bool 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); deSemaphore_destroy(testBuffer.empty); deSemaphore_destroy(testBuffer.fill); DE_TEST_ASSERT(testBuffer.producerHash == testBuffer.consumerHash); } } void deAtomic_selfTest(void) { /* Single-threaded tests. */ { volatile int32_t a = 11; DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == 12); DE_TEST_ASSERT(a == 12); DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == 13); DE_TEST_ASSERT(a == 13); a = -2; DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == -1); DE_TEST_ASSERT(a == -1); DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == 0); DE_TEST_ASSERT(a == 0); a = 11; DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == 10); DE_TEST_ASSERT(a == 10); DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == 9); DE_TEST_ASSERT(a == 9); a = 0; DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == -1); DE_TEST_ASSERT(a == -1); DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == -2); DE_TEST_ASSERT(a == -2); a = 0x7fffffff; DE_TEST_ASSERT(deAtomicIncrementInt32(&a) == (int)0x80000000); DE_TEST_ASSERT(a == (int)0x80000000); DE_TEST_ASSERT(deAtomicDecrementInt32(&a) == (int)0x7fffffff); DE_TEST_ASSERT(a == 0x7fffffff); } { volatile uint32_t a = 11; DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 12); DE_TEST_ASSERT(a == 12); DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 13); DE_TEST_ASSERT(a == 13); a = 0x7fffffff; DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 0x80000000); DE_TEST_ASSERT(a == 0x80000000); DE_TEST_ASSERT(deAtomicDecrementUint32(&a) == 0x7fffffff); DE_TEST_ASSERT(a == 0x7fffffff); a = 0xfffffffe; DE_TEST_ASSERT(deAtomicIncrementUint32(&a) == 0xffffffff); DE_TEST_ASSERT(a == 0xffffffff); DE_TEST_ASSERT(deAtomicDecrementUint32(&a) == 0xfffffffe); DE_TEST_ASSERT(a == 0xfffffffe); } { volatile uint32_t 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); } #if (DE_PTR_SIZE == 8) { volatile int64_t a = 11; DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == 12); DE_TEST_ASSERT(a == 12); DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == 13); DE_TEST_ASSERT(a == 13); a = -2; DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == -1); DE_TEST_ASSERT(a == -1); DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == 0); DE_TEST_ASSERT(a == 0); a = 11; DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == 10); DE_TEST_ASSERT(a == 10); DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == 9); DE_TEST_ASSERT(a == 9); a = 0; DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == -1); DE_TEST_ASSERT(a == -1); DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == -2); DE_TEST_ASSERT(a == -2); a = (int64_t)((1ull << 63) - 1ull); DE_TEST_ASSERT(deAtomicIncrementInt64(&a) == (int64_t)(1ull << 63)); DE_TEST_ASSERT(a == (int64_t)(1ull << 63)); DE_TEST_ASSERT(deAtomicDecrementInt64(&a) == (int64_t)((1ull << 63) - 1)); DE_TEST_ASSERT(a == (int64_t)((1ull << 63) - 1)); } #endif /* (DE_PTR_SIZE == 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 bool s_testSingletonInitialized = false; static volatile bool s_singletonInitLock = 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((uint32_t)initTimeMs); deAtomicIncrement32(&s_testSingletonInitCount); s_testSingletonInitialized = 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 = false; s_singletonInitLock = 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 = 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); } }