241 lines
5.9 KiB
C++
241 lines
5.9 KiB
C++
/*
|
|
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;
|
|
} |