Has anybody used CPPUnit to test if a unit is thread safe by having a test start worker threads and attempt to make "simultaneous" calls to the unit? I am having problems when I do this because CPPUnit raises exceptions when a CPPUNIT_ASSERT fails. This exception does not get caught by the framwork if it gets fired from a worker thread.
Is there any helper class in the framework for this type of test? Has anyone been through this before?
Thank you,
Pierre Boudreau
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I implemented this in a sub-class of CppUnit::TestCase, but my implementation is very Windows specific. It is also based on v1.6.2. Probably not useful as a general purpose addition to CppUnit.
My implementation overrides runTest(), creates threads (if requested as a cmd line parameter to the test) and runs a test from the threadProc.
The threads catch the exceptions, and uses thread return codes to communicate test status. If an exception was thrown, it is stored in per-thread data, and re-thrown when all threads complete. Not beautiful, but it works.
I also implemented a per-thread data class that is setup/torn down as the threads initialize.
If there is interest, I can post some source snippets.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Here's the content of my class header file/implementation file. Appreciate any feedback.
It relies on "gasp" global variables to pass in the number of threads, number of iterations, and a verbose flag that are supplied as command line params to the test case. Designed to work for console apps.
Again, Windows specific, CppUnit 1.6.2 compatible. Haven't tried working with 1.8.0.
// Forward declarations
class CppUnit::TestCase;
class CppUnit::Exception;
class MultiThreadedTestCase;
// Type declarations for the threads
typedef enum {
EXIT_TYPE_OK,
EXIT_TYPE_EXCEPTION,
EXIT_TYPE_STL_EXCEPTION,
EXIT_TYPE_UNKNOWN_EXCEPTION
} EXIT_TYPE;
typedef struct {
int nThreadNum;
int nMaxNumThreads;
int nIterations;
bool bVerbose;
MultiThreadedTestCase* pThis;
EXIT_TYPE eExitType;
CppUnit::Exception* pE;
} ThreadParams;
// The following class is used to hold thread specifc test data
// subclasses that require thread specific test data need to
// subclass this class for use in the threadSpecificSetup/TearDown calls
class ThreadTestData
{
public:
ThreadTestData() : m_initialized(false) {};
virtual ~ThreadTestData() {};
virtual bool initialize(bool bVerbose, unsigned int nThreadNum) = 0;
virtual bool deInitialize(bool bVerbose, unsigned int nThreadNum) = 0;
protected:
bool m_initialized;
};
// You should subclass the following class to implement a multithreaded
// or iterative test case.
class MultiThreadedTestCase : public CppUnit::TestCase
{
public:
MultiThreadedTestCase();
virtual ~MultiThreadedTestCase();
protected:
// The following method is called after setup(). It launches the threads if required
// Put thread independent initialization into setup(), and thread independent
// deinit into tearDown()
virtual void runTest(void);
// static thread entry point
static unsigned int WINAPI testThreadProc(void* arg);
// the following method implements the test that will be
// run in multiple threads.
virtual void threadedTest(ThreadTestData* pData, bool bVerbose, unsigned int nThreadNum);
// Override the following methods in subclasses
virtual bool threadSpecificSetup(ThreadTestData* pData, bool bVerbose, unsigned int nThreadNum);
virtual bool threadSpecificTearDown(ThreadTestData* pData, bool bVerbose, unsigned int nThreadNum);
// Provide an implementation of the following method in subclasses
// used to get thread specific test data. The caller is responsible for freeing the memory.
virtual ThreadTestData* getThreadTestData(void) { return NULL; };
// Checks for errors/exceptions in threads, and throws them to the TestCaller
virtual void checkThreadTestResults(void);
// Member variables that are of use in this class or subclasses
ThreadParams* m_threadData;
HANDLE* m_threadHandles;
MultiThreadedTestCase::~MultiThreadedTestCase()
{
// Delete any exceptions that might have occurred in testing
for (int i = 0; i < g_nThreadsToRun; i++) {
if (NULL != m_threadData[i].pE)
delete m_threadData[i].pE;
}
delete[] m_threadData;
delete[] m_threadHandles;
}
//////////////////////////////////////////////////////////////////////
// Public Method Implementation
//////////////////////////////////////////////////////////////////////
/**
* Thread Procedure - run the multi-threaded test
*/
unsigned int WINAPI MultiThreadedTestCase::testThreadProc(void* arg)
{
ThreadParams* pParams = static_cast<ThreadParams*>(arg);
ThreadTestData* pData = NULL;
int nRetCode = AN_SDK_SUCCESS;
if (pParams == NULL ) {
_tcerr << "T?:" << PREFIX_ERROR << "failed to get parameters--exiting" << endl;
return AN_SDK_ERROR;
}
/*suspend the thread and wait for a m_signal before you resume*/
pParams->pThis->suspendCurrentThread(pParams->nThreadNum, pParams->nMaxNumThreads);
if (pParams->bVerbose) {
cout << "T" << pParams->nThreadNum + 1 << ": success initializing the thread" << endl;
}
for (int i = 0; i < pParams->nIterations; i++) {
if (pParams->bVerbose) {
cout << "T" << pParams->nThreadNum + 1 << ": +++Starting Test Run " << i+1 << " +++" << endl;
}
pParams->pThis->threadedTest(pData, pParams->bVerbose, pParams->nThreadNum + 1);
}
if (!pParams->pThis->threadSpecificTearDown(pData, pParams->bVerbose, pParams->nThreadNum + 1)) {
_tcerr << "T" << pParams->nThreadNum + 1 << ": " << PREFIX_ERROR << "Thread specific test tear down failed" << endl;
return AN_SDK_ERROR;
}
/*
* The following catch statements are reusing what the CppUnit::TestCase::run()
* method does with exceptions.
* We have to prevent the exceptions from being unhandled and killing the thread
*/
} catch (CppUnit::Exception& e) {
pParams->eExitType = EXIT_TYPE_EXCEPTION;
pParams->pE = e.clone();
nRetCode = AN_SDK_ERROR;
} catch (std::exception& e) {
pParams->eExitType = EXIT_TYPE_STL_EXCEPTION;
pParams->pE = new CppUnit::Exception(e.what());
nRetCode = AN_SDK_ERROR;
} catch (...) {
pParams->eExitType = EXIT_TYPE_UNKNOWN_EXCEPTION;
pParams->pE = new CppUnit::Exception ("caught unknown exception");
nRetCode = AN_SDK_ERROR;
}
// Release thread specific data.
delete pData;
/*m_signal the end of process when the last thread is here*/
if (InterlockedIncrement(&(pParams->pThis->m_completedThreads)) == pParams->nMaxNumThreads) {
pParams->pThis->signalEnd();
}
pParams->pThis->m_threadHandles[pParams->nThreadNum] = 0;
return nRetCode;
}
//////////////////////////////////////////////////////////////////////
// Protected Method Implementation
//////////////////////////////////////////////////////////////////////
/***
* Override for the TestCase::runTest method -- implements the multi-threaded
* behavior.
*/
void MultiThreadedTestCase::runTest(void)
{
ThreadTestData* pData;
// Set up the required number of threads to run
if (1 == g_nThreadsToRun) {
// Want single threaded test. Can let exceptions flow up to caller
pData = getThreadTestData();
if (g_bVerbose) cout << "T0: success initializing the test" << endl;
for (int i = 0; i < g_nIterations; i++) {
if (g_bVerbose) cout << "T0: +++Starting Test Run " << i+1 << "+++" << endl;
threadedTest(pData, g_bVerbose, 0);
}
// Must run multiple threads
/* create the events that we'll be using for signaling*/
createEvents();
/*Start the Guardian threads*/
if (g_bVerbose) cout << endl << "===Initializing" << endl;
createThreads();
/*wait for threads to be initialized*/
if (waitUntilCreated()) {
if (g_bVerbose) {
cout << endl << "===Initialization completed" << endl;
cout << endl << "===Processing" << endl;
}
resumeThreads();
} else {
// Don't throw an exception in this case or we may be leaving some threads in limbo
_tcerr << PREFIX_ERROR << "Failure in waitUntilInit(). Terminating threads and exiting" << endl;
}
/* wait for all the threads to be done */
waitUntilDone();
// Now we're done, so let's check the results.
checkThreadTestResults();
}
}
/***
* Checks the return code from all threads called.
* NOTE: the destructor releases all exceptions that occurred.
*/
void MultiThreadedTestCase::checkThreadTestResults(void)
{
// Each thread has updated the results of their tests in the
// thread data.
CppUnit::Exception* pE = NULL;
for (int i = 0; i < g_nThreadsToRun; i++) {
switch (m_threadData[i].eExitType) {
case EXIT_TYPE_OK:
if (g_bVerbose) cout << "T" << i + 1 << ": completed test successfully" << endl;
break;
case EXIT_TYPE_EXCEPTION:
case EXIT_TYPE_STL_EXCEPTION:
case EXIT_TYPE_UNKNOWN_EXCEPTION:
_tcerr << "T" << i + 1 << ": " << PREFIX_ERROR << "Exception occurred, message: "
<< m_threadData[i].pE->what() << endl;
if (NULL == pE)
pE = m_threadData[i].pE;
break;
}
}
// Just throw the first one found.
if (NULL != pE)
throw *pE;
}
/***
* Thread specific initialization
*/
bool MultiThreadedTestCase::threadSpecificSetup(
ThreadTestData* pData,
bool bVerbose,
unsigned int nThreadNum)
{
if (NULL != pData) {
return pData->initialize(bVerbose, nThreadNum);
}
return true;
}
/***
* Test to run. Should be overridden in subclasses
*/
bool MultiThreadedTestCase::threadSpecificTearDown(
ThreadTestData* pData,
bool bVerbose,
unsigned int nThreadNum)
{
if (NULL != pData)
return pData->deInitialize(bVerbose, nThreadNum);
return true;
}
/***
* Test to run. Should be overridden in subclasses
*/
void MultiThreadedTestCase::threadedTest(
ThreadTestData* pData,
bool bVerbose,
unsigned int nThreadNum)
{
if (bVerbose) cout << "T" << nThreadNum << ": starting threadedTest" << endl;
CPPUNIT_ASSERT_MESSAGE("threadedTest--THIS METHOD SHOULD BE OVERRIDDEN", false);
}
//////////////////////////////////////////////////////////////////////
// Private Method Implementation
//////////////////////////////////////////////////////////////////////
void MultiThreadedTestCase::createEvents()
{
/*event to m_signal the completion of the last thread*/
m_signalEndEvent = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
FALSE, // initial state is m_signaled
NULL); // object not named
/*event to m_signal the initialization of the last thread*/
m_signalInitCompletedEvent = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
FALSE, // initial state is m_signaled
NULL); // object not named
/*event to m_signal the initialization of the first thread is completed*/
m_signalFirstThreadEvent = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
FALSE, // initial state is m_signaled
NULL); // object not named
}
void MultiThreadedTestCase::signalEnd(){
/*enable the event for the waiting function*/
SetEvent(m_signalEndEvent);
}
void MultiThreadedTestCase:: suspendCurrentThread(
const unsigned int nThread,
const unsigned int nThreadsToRun)
{
const LONG res = InterlockedIncrement(&m_suspendedThreads);
if (res == 1) {
/*enable the event for the waiting function*/
SetEvent(m_signalFirstThreadEvent);
}
if (res == nThreadsToRun) {
/*enable the event for the waiting function*/
SetEvent(m_signalInitCompletedEvent);
}
SuspendThread(m_threadHandles[nThread]);
}
/*wait until all the threads are initiated and ready to go*/
bool MultiThreadedTestCase::waitUntilCreated()
{
if(WaitForSingleObject(m_signalInitCompletedEvent, INFINITE) == WAIT_OBJECT_0) {
return true;
}
return false;
}
void MultiThreadedTestCase::createThreads()
{
unsigned int unThreadId;
for (unsigned int i = 0; i < g_nThreadsToRun; i++) {
// Call beginthreadex as CreateThread leaks memory if we're using the
// C runtime library (we are).
m_threadHandles[i] = (HANDLE)_beginthreadex(
NULL, // no security attributes
0, // use default stack size
MultiThreadedTestCase::testThreadProc, // thread function
(void*)&m_threadData[i], // argument to thread function
0, // use default creation flags
&unThreadId); // returns the thread identifier
if (0 == m_threadHandles[i]) {
// If a thread failed, notify but don't cancel the test.
_tcerr << "T" << i + 1 << ": " << PREFIX_ERROR << "could not create thread using _beginthreadex" << endl;
}
if (i == 0) {
/* wait for a signal after the first init */
if(WaitForSingleObject(m_signalFirstThreadEvent, INFINITE) == WAIT_OBJECT_0) {
Sleep(3000);
}
}else{
Sleep(1000);
}
}
}
void MultiThreadedTestCase::resumeThreads() {
for (unsigned int i=0; i < g_nThreadsToRun; i++) {
ResumeThread(m_threadHandles[i]);
}
}
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Has anybody used CPPUnit to test if a unit is thread safe by having a test start worker threads and attempt to make "simultaneous" calls to the unit? I am having problems when I do this because CPPUnit raises exceptions when a CPPUNIT_ASSERT fails. This exception does not get caught by the framwork if it gets fired from a worker thread.
Is there any helper class in the framework for this type of test? Has anyone been through this before?
Thank you,
Pierre Boudreau
I implemented this in a sub-class of CppUnit::TestCase, but my implementation is very Windows specific. It is also based on v1.6.2. Probably not useful as a general purpose addition to CppUnit.
My implementation overrides runTest(), creates threads (if requested as a cmd line parameter to the test) and runs a test from the threadProc.
The threads catch the exceptions, and uses thread return codes to communicate test status. If an exception was thrown, it is stored in per-thread data, and re-thrown when all threads complete. Not beautiful, but it works.
I also implemented a per-thread data class that is setup/torn down as the threads initialize.
If there is interest, I can post some source snippets.
I am very interested in seeing the code sniplets. I was about to roll up my sleeves and start doing something very similar.
Here's the content of my class header file/implementation file. Appreciate any feedback.
It relies on "gasp" global variables to pass in the number of threads, number of iterations, and a verbose flag that are supplied as command line params to the test case. Designed to work for console apps.
Again, Windows specific, CppUnit 1.6.2 compatible. Haven't tried working with 1.8.0.
Formatting horribly broken
===========================================h.
#ifndef __MultiThreadedTestCase_H__
#define __MultiThreadedTestCase_H__
// Forward declarations
class CppUnit::TestCase;
class CppUnit::Exception;
class MultiThreadedTestCase;
// Type declarations for the threads
typedef enum {
EXIT_TYPE_OK,
EXIT_TYPE_EXCEPTION,
EXIT_TYPE_STL_EXCEPTION,
EXIT_TYPE_UNKNOWN_EXCEPTION
} EXIT_TYPE;
typedef struct {
int nThreadNum;
int nMaxNumThreads;
int nIterations;
bool bVerbose;
MultiThreadedTestCase* pThis;
EXIT_TYPE eExitType;
CppUnit::Exception* pE;
} ThreadParams;
// The following class is used to hold thread specifc test data
// subclasses that require thread specific test data need to
// subclass this class for use in the threadSpecificSetup/TearDown calls
class ThreadTestData
{
public:
ThreadTestData() : m_initialized(false) {};
virtual ~ThreadTestData() {};
virtual bool initialize(bool bVerbose, unsigned int nThreadNum) = 0;
virtual bool deInitialize(bool bVerbose, unsigned int nThreadNum) = 0;
protected:
bool m_initialized;
};
// You should subclass the following class to implement a multithreaded
// or iterative test case.
class MultiThreadedTestCase : public CppUnit::TestCase
{
public:
MultiThreadedTestCase();
virtual ~MultiThreadedTestCase();
protected:
// The following method is called after setup(). It launches the threads if required
// Put thread independent initialization into setup(), and thread independent
// deinit into tearDown()
virtual void runTest(void);
// static thread entry point
static unsigned int WINAPI testThreadProc(void* arg);
// the following method implements the test that will be
// run in multiple threads.
virtual void threadedTest(ThreadTestData* pData, bool bVerbose, unsigned int nThreadNum);
// Override the following methods in subclasses
virtual bool threadSpecificSetup(ThreadTestData* pData, bool bVerbose, unsigned int nThreadNum);
virtual bool threadSpecificTearDown(ThreadTestData* pData, bool bVerbose, unsigned int nThreadNum);
// Provide an implementation of the following method in subclasses
// used to get thread specific test data. The caller is responsible for freeing the memory.
virtual ThreadTestData* getThreadTestData(void) { return NULL; };
// Checks for errors/exceptions in threads, and throws them to the TestCaller
virtual void checkThreadTestResults(void);
// Member variables that are of use in this class or subclasses
ThreadParams* m_threadData;
HANDLE* m_threadHandles;
private:
MultiThreadedTestCase(const MultiThreadedTestCase& other);
MultiThreadedTestCase& operator= (const MultiThreadedTestCase& other);
void resumeThreads();
void signalEnd();
void suspendCurrentThread(const unsigned int nThread, const unsigned int nThreadsToRun);
const int waitUntilDone();
bool waitUntilCreated();
void createEvents();
void createThreads();
HANDLE m_signalInitCompletedEvent;
HANDLE m_signalEndEvent;
HANDLE m_signalFirstThreadEvent;
LONG m_suspendedThreads;
LONG m_completedThreads;
};
#endif
====================================cpp file
//////////////////////////////////////////////////////////////////////
// MultiThreadedTestCase.cpp: implementation of the MultiThreadedTestCase class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "cppunit/TestCase.h"
#include "cppunit/Exception.h"
#include "MultiThreadedTestCase.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
MultiThreadedTestCase::MultiThreadedTestCase(void) :
m_suspendedThreads(0L),
m_completedThreads(0L)
{
m_threadData = new ThreadParams[g_nThreadsToRun];
for (unsigned int i=0; i < g_nThreadsToRun; i++) {
m_threadData[i].bVerbose = g_bVerbose;
m_threadData[i].nIterations = g_nIterations;
m_threadData[i].nMaxNumThreads = g_nThreadsToRun;
m_threadData[i].nThreadNum = i;
m_threadData[i].pThis = this;
m_threadData[i].eExitType = EXIT_TYPE_OK;
m_threadData[i].pE = NULL;
}
m_threadHandles = new HANDLE[g_nThreadsToRun];
memset(m_threadHandles, 0, sizeof(HANDLE) * g_nThreadsToRun);
}
MultiThreadedTestCase::~MultiThreadedTestCase()
{
// Delete any exceptions that might have occurred in testing
for (int i = 0; i < g_nThreadsToRun; i++) {
if (NULL != m_threadData[i].pE)
delete m_threadData[i].pE;
}
delete[] m_threadData;
delete[] m_threadHandles;
}
//////////////////////////////////////////////////////////////////////
// Public Method Implementation
//////////////////////////////////////////////////////////////////////
/**
* Thread Procedure - run the multi-threaded test
*/
unsigned int WINAPI MultiThreadedTestCase::testThreadProc(void* arg)
{
ThreadParams* pParams = static_cast<ThreadParams*>(arg);
ThreadTestData* pData = NULL;
int nRetCode = AN_SDK_SUCCESS;
if (pParams == NULL ) {
_tcerr << "T?:" << PREFIX_ERROR << "failed to get parameters--exiting" << endl;
return AN_SDK_ERROR;
}
/*suspend the thread and wait for a m_signal before you resume*/
pParams->pThis->suspendCurrentThread(pParams->nThreadNum, pParams->nMaxNumThreads);
try {
pData = pParams->pThis->getThreadTestData();
if (!pParams->pThis->threadSpecificSetup(pData, pParams->bVerbose, pParams->nThreadNum + 1)) {
_tcerr << "T" << pParams->nThreadNum + 1 << ": " << PREFIX_ERROR << "Thread specific test initialization failed" << endl;
return AN_SDK_ERROR;
}
if (pParams->bVerbose) {
cout << "T" << pParams->nThreadNum + 1 << ": success initializing the thread" << endl;
}
for (int i = 0; i < pParams->nIterations; i++) {
if (pParams->bVerbose) {
cout << "T" << pParams->nThreadNum + 1 << ": +++Starting Test Run " << i+1 << " +++" << endl;
}
pParams->pThis->threadedTest(pData, pParams->bVerbose, pParams->nThreadNum + 1);
}
if (!pParams->pThis->threadSpecificTearDown(pData, pParams->bVerbose, pParams->nThreadNum + 1)) {
_tcerr << "T" << pParams->nThreadNum + 1 << ": " << PREFIX_ERROR << "Thread specific test tear down failed" << endl;
return AN_SDK_ERROR;
}
/*
* The following catch statements are reusing what the CppUnit::TestCase::run()
* method does with exceptions.
* We have to prevent the exceptions from being unhandled and killing the thread
*/
} catch (CppUnit::Exception& e) {
pParams->eExitType = EXIT_TYPE_EXCEPTION;
pParams->pE = e.clone();
nRetCode = AN_SDK_ERROR;
} catch (std::exception& e) {
pParams->eExitType = EXIT_TYPE_STL_EXCEPTION;
pParams->pE = new CppUnit::Exception(e.what());
nRetCode = AN_SDK_ERROR;
} catch (...) {
pParams->eExitType = EXIT_TYPE_UNKNOWN_EXCEPTION;
pParams->pE = new CppUnit::Exception ("caught unknown exception");
nRetCode = AN_SDK_ERROR;
}
// Release thread specific data.
delete pData;
/*m_signal the end of process when the last thread is here*/
if (InterlockedIncrement(&(pParams->pThis->m_completedThreads)) == pParams->nMaxNumThreads) {
pParams->pThis->signalEnd();
}
pParams->pThis->m_threadHandles[pParams->nThreadNum] = 0;
return nRetCode;
}
//////////////////////////////////////////////////////////////////////
// Protected Method Implementation
//////////////////////////////////////////////////////////////////////
/***
* Override for the TestCase::runTest method -- implements the multi-threaded
* behavior.
*/
void MultiThreadedTestCase::runTest(void)
{
ThreadTestData* pData;
// Set up the required number of threads to run
if (1 == g_nThreadsToRun) {
// Want single threaded test. Can let exceptions flow up to caller
pData = getThreadTestData();
try {
if (!threadSpecificSetup(pData, g_bVerbose, 0)) {
delete pData;
CPPUNIT_ASSERT_MESSAGE("runTest - threadSpecificSetup failed", false);
}
if (g_bVerbose) cout << "T0: success initializing the test" << endl;
for (int i = 0; i < g_nIterations; i++) {
if (g_bVerbose) cout << "T0: +++Starting Test Run " << i+1 << "+++" << endl;
threadedTest(pData, g_bVerbose, 0);
}
if (!threadSpecificTearDown(pData, g_bVerbose, 0)) {
delete pData;
CPPUNIT_ASSERT_MESSAGE("runTest - threadSpecificTearDown failed", false);
}
} catch (...) {
delete pData;
throw;
}
delete pData;
} else {
// Must run multiple threads
/* create the events that we'll be using for signaling*/
createEvents();
/*Start the Guardian threads*/
if (g_bVerbose) cout << endl << "===Initializing" << endl;
createThreads();
/*wait for threads to be initialized*/
if (waitUntilCreated()) {
if (g_bVerbose) {
cout << endl << "===Initialization completed" << endl;
cout << endl << "===Processing" << endl;
}
resumeThreads();
} else {
// Don't throw an exception in this case or we may be leaving some threads in limbo
_tcerr << PREFIX_ERROR << "Failure in waitUntilInit(). Terminating threads and exiting" << endl;
}
/* wait for all the threads to be done */
waitUntilDone();
// Now we're done, so let's check the results.
checkThreadTestResults();
}
}
/***
* Checks the return code from all threads called.
* NOTE: the destructor releases all exceptions that occurred.
*/
void MultiThreadedTestCase::checkThreadTestResults(void)
{
// Each thread has updated the results of their tests in the
// thread data.
CppUnit::Exception* pE = NULL;
for (int i = 0; i < g_nThreadsToRun; i++) {
switch (m_threadData[i].eExitType) {
case EXIT_TYPE_OK:
if (g_bVerbose) cout << "T" << i + 1 << ": completed test successfully" << endl;
break;
case EXIT_TYPE_EXCEPTION:
case EXIT_TYPE_STL_EXCEPTION:
case EXIT_TYPE_UNKNOWN_EXCEPTION:
_tcerr << "T" << i + 1 << ": " << PREFIX_ERROR << "Exception occurred, message: "
<< m_threadData[i].pE->what() << endl;
if (NULL == pE)
pE = m_threadData[i].pE;
break;
}
}
// Just throw the first one found.
if (NULL != pE)
throw *pE;
}
/***
* Thread specific initialization
*/
bool MultiThreadedTestCase::threadSpecificSetup(
ThreadTestData* pData,
bool bVerbose,
unsigned int nThreadNum)
{
if (NULL != pData) {
return pData->initialize(bVerbose, nThreadNum);
}
return true;
}
/***
* Test to run. Should be overridden in subclasses
*/
bool MultiThreadedTestCase::threadSpecificTearDown(
ThreadTestData* pData,
bool bVerbose,
unsigned int nThreadNum)
{
if (NULL != pData)
return pData->deInitialize(bVerbose, nThreadNum);
return true;
}
/***
* Test to run. Should be overridden in subclasses
*/
void MultiThreadedTestCase::threadedTest(
ThreadTestData* pData,
bool bVerbose,
unsigned int nThreadNum)
{
if (bVerbose) cout << "T" << nThreadNum << ": starting threadedTest" << endl;
CPPUNIT_ASSERT_MESSAGE("threadedTest--THIS METHOD SHOULD BE OVERRIDDEN", false);
}
//////////////////////////////////////////////////////////////////////
// Private Method Implementation
//////////////////////////////////////////////////////////////////////
void MultiThreadedTestCase::createEvents()
{
/*event to m_signal the completion of the last thread*/
m_signalEndEvent = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
FALSE, // initial state is m_signaled
NULL); // object not named
/*event to m_signal the initialization of the last thread*/
m_signalInitCompletedEvent = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
FALSE, // initial state is m_signaled
NULL); // object not named
/*event to m_signal the initialization of the first thread is completed*/
m_signalFirstThreadEvent = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
FALSE, // initial state is m_signaled
NULL); // object not named
}
void MultiThreadedTestCase::signalEnd(){
/*enable the event for the waiting function*/
SetEvent(m_signalEndEvent);
}
void MultiThreadedTestCase:: suspendCurrentThread(
const unsigned int nThread,
const unsigned int nThreadsToRun)
{
const LONG res = InterlockedIncrement(&m_suspendedThreads);
if (res == 1) {
/*enable the event for the waiting function*/
SetEvent(m_signalFirstThreadEvent);
}
if (res == nThreadsToRun) {
/*enable the event for the waiting function*/
SetEvent(m_signalInitCompletedEvent);
}
SuspendThread(m_threadHandles[nThread]);
}
const int MultiThreadedTestCase::waitUntilDone()
{
if(WaitForSingleObject(m_signalEndEvent, INFINITE) == WAIT_OBJECT_0){
return 1;
}
return 0;
}
/*wait until all the threads are initiated and ready to go*/
bool MultiThreadedTestCase::waitUntilCreated()
{
if(WaitForSingleObject(m_signalInitCompletedEvent, INFINITE) == WAIT_OBJECT_0) {
return true;
}
return false;
}
void MultiThreadedTestCase::createThreads()
{
unsigned int unThreadId;
for (unsigned int i = 0; i < g_nThreadsToRun; i++) {
// Call beginthreadex as CreateThread leaks memory if we're using the
// C runtime library (we are).
m_threadHandles[i] = (HANDLE)_beginthreadex(
NULL, // no security attributes
0, // use default stack size
MultiThreadedTestCase::testThreadProc, // thread function
(void*)&m_threadData[i], // argument to thread function
0, // use default creation flags
&unThreadId); // returns the thread identifier
if (0 == m_threadHandles[i]) {
// If a thread failed, notify but don't cancel the test.
_tcerr << "T" << i + 1 << ": " << PREFIX_ERROR << "could not create thread using _beginthreadex" << endl;
}
if (i == 0) {
/* wait for a signal after the first init */
if(WaitForSingleObject(m_signalFirstThreadEvent, INFINITE) == WAIT_OBJECT_0) {
Sleep(3000);
}
}else{
Sleep(1000);
}
}
}
void MultiThreadedTestCase::resumeThreads() {
for (unsigned int i=0; i < g_nThreadsToRun; i++) {
ResumeThread(m_threadHandles[i]);
}
}