Initial commit
This commit is contained in:
241
ZTestSuite/Test-ZConcurrency.cpp
Normal file
241
ZTestSuite/Test-ZConcurrency.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
Test-ZConcurrency.cpp
|
||||
Author: Patrick Baggett
|
||||
|
||||
Purpose : Unit tests for ZConcurrency implementation to ensure correct behavior
|
||||
|
||||
Changelog :
|
||||
12/18/2011 - Removed dependency on ZString (crertel)
|
||||
5/15/2011 - Created (ptbaggett)
|
||||
*/
|
||||
|
||||
#include "ZUnitTest.hpp"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ZUtil/ZConcurrency.hpp>
|
||||
|
||||
static const char* testCreateThread();
|
||||
static const char* testSleepGetTicks();
|
||||
static const char* testUncontLockUnlockMutex();
|
||||
static const char* testContLockUnlockMutex();
|
||||
static const char* testUncontSema();
|
||||
static const char* testContSema();
|
||||
|
||||
#define NRTHR 16
|
||||
#define NRITER 10000
|
||||
|
||||
//List of unit tests
|
||||
ZUnitTest ZConcurrencyUnitTests[] =
|
||||
{
|
||||
{ "CreateThread()/WaitThread()", testCreateThread },
|
||||
{ "Sleep()/GetTicks()", testSleepGetTicks },
|
||||
{ "Uncontested LockMutex()/UnlockMutex()", testUncontLockUnlockMutex },
|
||||
{ "Heavily contested LockMutex/UnlockMutex()", testContLockUnlockMutex },
|
||||
{ "Uncontested Semaphore", testUncontSema },
|
||||
{ "Heavily contested Semaphore", testContSema }
|
||||
|
||||
};
|
||||
|
||||
DECLARE_ZTESTBLOCK(ZConcurrency);
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static const char* testThread1Data;
|
||||
static int testThread1(void* arg)
|
||||
{
|
||||
unsigned int* val = (unsigned int*)arg;
|
||||
|
||||
if(*val == 0xAABBCCDD)
|
||||
testThread1Data = ZTEST_SUCCESS;
|
||||
else
|
||||
testThread1Data = "Error with CreateThread()'s argument passing";
|
||||
|
||||
return 0x11223344;
|
||||
}
|
||||
|
||||
static const char* testCreateThread()
|
||||
{
|
||||
ZThreadContext ctx;
|
||||
int arg = 0xAABBCCDD;
|
||||
|
||||
ctx = ZConcurrency::CreateThread(testThread1, &arg);
|
||||
|
||||
TASSERT(ctx.IsValid(), "CreateThread() returned invalid thread context");
|
||||
|
||||
ZConcurrency::WaitThread(ctx);
|
||||
|
||||
int thrRetVal = ctx.ThreadExitStatus;
|
||||
|
||||
TASSERT(thrRetVal == 0x11223344, "WaitThread() returned wrong value");
|
||||
|
||||
ZConcurrency::DestroyThread(ctx);
|
||||
|
||||
TASSERT(!ctx.IsValid(), "ZThreadContext is valid after DestroyThread()");
|
||||
|
||||
return testThread1Data;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static const char* testSleepGetTicks()
|
||||
{
|
||||
uint64_t timeStart, timeEnd;
|
||||
|
||||
timeStart = ZConcurrency::GetTicks();
|
||||
ZConcurrency::SleepThread(1000);
|
||||
timeEnd = ZConcurrency::GetTicks();
|
||||
|
||||
TASSERT(timeStart < timeEnd, "GetTicks() not monotonically increasing");
|
||||
|
||||
//We can't require precisely 1,000ms, but we do require it as a lower bound
|
||||
TASSERT(timeEnd - timeStart >= 1000, "GetTicks() reports < 1000ms elapsed, but SleepThread(1000) called.");
|
||||
|
||||
return ZTEST_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static const char* testUncontLockUnlockMutex()
|
||||
{
|
||||
//Basically, if this crashes, then FAIL
|
||||
ZMutex lock;
|
||||
|
||||
//TASSERT(lock != NULL, "CreateMutex() returned null pointer");
|
||||
|
||||
//Lock/unlock a bunch. This should be fast since it is uncontested.
|
||||
//Mainly, we're checking here to see if lock/unlock repeatedly triggers a crash
|
||||
for(int i=0; i<1000; i++)
|
||||
{
|
||||
lock.Acquire();
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
return ZTEST_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static ZMutex testContLock;
|
||||
|
||||
static int testContFunc(void* arg)
|
||||
{
|
||||
int* inc = (int*) arg;
|
||||
|
||||
for(int i=0; i<NRITER; i++)
|
||||
{
|
||||
testContLock.Acquire();
|
||||
*inc += 1;
|
||||
testContLock.Release();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* testContLockUnlockMutex()
|
||||
{
|
||||
ZThreadContext threads[NRTHR];
|
||||
int arg = 0;
|
||||
|
||||
for(int i=0; i<NRTHR; i++)
|
||||
threads[i] = ZConcurrency::CreateThread(testContFunc, &arg);
|
||||
|
||||
for(int i=0; i<NRTHR; i++)
|
||||
{
|
||||
ZConcurrency::WaitThread(threads[i]);
|
||||
ZConcurrency::DestroyThread(threads[i]);
|
||||
}
|
||||
|
||||
TASSERT(arg == NRTHR * NRITER, "Invalid counter value, mutex not functioning")
|
||||
|
||||
return ZTEST_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static const char* testUncontSema()
|
||||
{
|
||||
ZSemaphore sem(3);
|
||||
bool ok = true;
|
||||
|
||||
//TASSERT(sem != NULL, "CreateSemaphore() failed");
|
||||
|
||||
//Decrement count back down to 0 from 3
|
||||
ok = ok && sem.Wait(ZWAIT_INFINITE);
|
||||
TASSERT(ok, "First WaitSemaphore() failed");
|
||||
ok = ok && sem.Wait(ZWAIT_INFINITE);
|
||||
TASSERT(ok, "Second WaitSemaphore() failed");
|
||||
ok = ok && sem.Wait(ZWAIT_INFINITE);
|
||||
TASSERT(ok, "Third WaitSemaphore() failed");
|
||||
|
||||
//This should immediately return with a failure message
|
||||
ok = sem.Wait(0);
|
||||
TASSERT(!ok, "sem.Wait() with count of 0 succeeded, but should have failed.");
|
||||
|
||||
//Post to the semaphore, the decrement it again
|
||||
sem.Post(2);
|
||||
ok = sem.Wait(ZWAIT_INFINITE);
|
||||
TASSERT(ok, "First sem.Wait() after PostSemaphore() failed");
|
||||
ok = ok && sem.Wait(ZWAIT_INFINITE);
|
||||
TASSERT(ok, "Second sem.Wait() after PostSemaphore() failed");
|
||||
|
||||
//This should immediately return with a failure message
|
||||
ok = sem.Wait(0);
|
||||
TASSERT(!ok, "sem.Wait() (second round) with count of 0 succeeded, but should have failed.");
|
||||
|
||||
return ZTEST_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
static volatile int testContSemaCounter;
|
||||
static ZMutex testContSemaLock;
|
||||
|
||||
#include <ZUtil/ZConcurrency.hpp>
|
||||
static int testContSemaFunc(void* arg)
|
||||
{
|
||||
ZSemaphore* sem = (ZSemaphore*)arg;
|
||||
bool ok;
|
||||
do
|
||||
{
|
||||
ok = sem->Wait(0);
|
||||
if (ok)
|
||||
{
|
||||
testContSemaLock.Acquire();
|
||||
testContSemaCounter++;
|
||||
testContSemaLock.Release();
|
||||
}
|
||||
} while(ok);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* testContSema()
|
||||
{
|
||||
ZThreadContext thread[NRTHR];
|
||||
|
||||
|
||||
|
||||
//sem = ZConcurrency::CreateSemaphore(NRITER);
|
||||
//testContSemaLock = ZConcurrency::CreateMutex();
|
||||
|
||||
//Run 50 batches of this test just in case there might be a race condition
|
||||
for(int j = 0; j < 50; j++)
|
||||
{
|
||||
ZSemaphore sem(NRITER);
|
||||
testContSemaCounter = 0;
|
||||
|
||||
//Create a bunch of threads.
|
||||
for(int i=0; i<NRTHR; i++)
|
||||
thread[i] = ZConcurrency::CreateThread(testContSemaFunc, &sem);
|
||||
|
||||
//Wait and close their contexts
|
||||
for(int i = 0; i < NRTHR; i++)
|
||||
{
|
||||
ZConcurrency::WaitThread(thread[i]);
|
||||
ZConcurrency::DestroyThread(thread[i]);
|
||||
}
|
||||
TASSERT(testContSemaCounter == NRITER, "Counter after semaphore is incorrect");
|
||||
TASSERT(sem.Wait(0) == false, "Semaphore still had non-zero count!");
|
||||
}
|
||||
|
||||
return ZTEST_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user