1b89aa98ff
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6640 a95241bf-73f2-0310-859d-f6bbb57e9c96
198 lines
5.3 KiB
C++
198 lines
5.3 KiB
C++
#ifndef _beos_thread_manager_h_
|
|
#define _beos_thread_manager_h_
|
|
|
|
#include <cppunit/Exception.h>
|
|
#include <cppunit/TestResult.h>
|
|
#include <OS.h>
|
|
#include <signal.h>
|
|
#include <string>
|
|
|
|
// Pointer to a function that takes no parameters and
|
|
// returns no result. All threads must be implemented
|
|
// in a function of this type.
|
|
|
|
// Helper class to handle thread management
|
|
template <class TestClass, class ExpectedException>
|
|
class CPPUNIT_API BThreadManager {
|
|
public:
|
|
typedef void (TestClass::*ThreadMethod)();
|
|
|
|
BThreadManager(std::string threadName, TestClass *object, ThreadMethod method, sem_id &threadSem);
|
|
~BThreadManager();
|
|
|
|
status_t LaunchThread(CppUnit::TestResult *result);
|
|
|
|
// Thread management methods
|
|
int32 Stop();
|
|
int32 WaitForThread();
|
|
bool IsRunning();
|
|
|
|
std::string getName() const { return fName; }
|
|
|
|
protected:
|
|
std::string fName;
|
|
TestClass *fObject;
|
|
ThreadMethod fMethod;
|
|
thread_id fID;
|
|
CppUnit::TestResult *fTestResult;
|
|
sem_id &fThreadSem;
|
|
|
|
static long EntryFunction(BThreadManager<TestClass, ExpectedException>* manager);
|
|
void Run();
|
|
|
|
};
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
BThreadManager<TestClass, ExpectedException>::BThreadManager(
|
|
std::string threadName,
|
|
TestClass *object,
|
|
ThreadMethod method,
|
|
sem_id &threadSem
|
|
)
|
|
: fName(threadName)
|
|
, fObject(object)
|
|
, fMethod(method)
|
|
, fID(0)
|
|
, fTestResult(NULL)
|
|
, fThreadSem(threadSem)
|
|
{
|
|
}
|
|
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
BThreadManager<TestClass, ExpectedException>::~BThreadManager() {
|
|
Stop();
|
|
}
|
|
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
int32
|
|
BThreadManager<TestClass, ExpectedException>::WaitForThread() {
|
|
int32 result = 0;
|
|
if (find_thread(NULL) != fID)
|
|
wait_for_thread(fID, &result);
|
|
return result;
|
|
}
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
int32
|
|
BThreadManager<TestClass, ExpectedException>::Stop() {
|
|
int32 result = 0;
|
|
if (find_thread(NULL) != fID) {
|
|
while (IsRunning()) {
|
|
kill(fID, SIGINT);
|
|
snooze(1000000);
|
|
}
|
|
result = WaitForThread();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
bool
|
|
BThreadManager<TestClass, ExpectedException>::IsRunning(void) {
|
|
if (fID != 0) {
|
|
thread_info info;
|
|
if (get_thread_info(fID, &info) == B_OK)
|
|
return true;
|
|
else
|
|
fID = 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
status_t
|
|
BThreadManager<TestClass, ExpectedException>::LaunchThread(CppUnit::TestResult *result) {
|
|
if (IsRunning())
|
|
return B_ALREADY_RUNNING;
|
|
|
|
fTestResult = result;
|
|
fID = spawn_thread((thread_entry)(BThreadManager::EntryFunction),
|
|
fName.c_str(), B_NORMAL_PRIORITY, this);
|
|
|
|
status_t err;
|
|
if (fID == B_NO_MORE_THREADS || fID == B_NO_MEMORY) {
|
|
err = fID;
|
|
fID = 0;
|
|
} else {
|
|
// Aquire the semaphore, then start the thread.
|
|
if (acquire_sem(fThreadSem) != B_OK)
|
|
throw CppUnit::Exception("BThreadManager::LaunchThread() -- Error acquiring thread semaphore");
|
|
err = resume_thread(fID);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
long
|
|
BThreadManager<TestClass, ExpectedException>::EntryFunction(BThreadManager<TestClass, ExpectedException> *manager) {
|
|
manager->Run();
|
|
return 0;
|
|
}
|
|
|
|
template <class TestClass, class ExpectedException>
|
|
void
|
|
BThreadManager<TestClass, ExpectedException>::Run(void) {
|
|
// These outer try/catch blocks handle unexpected exceptions.
|
|
// Said exceptions are caught and noted in the TestResult
|
|
// class, but not allowed to escape and disrupt the other
|
|
// threads that are assumingly running concurrently.
|
|
try {
|
|
|
|
// Our parent ThreadedTestCaller should check fObject to be non-NULL,
|
|
// but we'll do it here too just to be sure.
|
|
if (!fObject)
|
|
throw CppUnit::Exception("BThreadManager::Run() -- NULL fObject pointer");
|
|
|
|
// Before running, we need to add this thread's name to
|
|
// the object's id->(name,subtestnum) map.
|
|
fObject->InitThreadInfo(fID, fName);
|
|
|
|
// This inner try/catch block is for expected exceptions.
|
|
// If we get through it without an exception, we have to
|
|
// raise a different exception that makes note of the fact
|
|
// that the exception we were expecting didn't arrive. If
|
|
// no exception is expected, then nothing is done (see
|
|
// "cppunit/TestCaller.h" for detail on the classes used
|
|
// to handle this behaviour).
|
|
try {
|
|
(fObject->*fMethod)();
|
|
} catch ( ExpectedException & ) {
|
|
return;
|
|
}
|
|
CppUnit::ExpectedExceptionTraits<ExpectedException>::expectedException();
|
|
|
|
} catch ( CppUnit::Exception &e ) {
|
|
// Add on the thread name, then note the exception
|
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
|
std::string(e.what()) + " (thread: " + fName + ")",
|
|
e.sourceLine()
|
|
);
|
|
fTestResult->addFailure( fObject, threadException );
|
|
}
|
|
catch ( std::exception &e ) {
|
|
// Add on the thread name, then note the exception
|
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
|
std::string(e.what()) + " (thread: " + fName + ")"
|
|
);
|
|
fTestResult->addError( fObject, threadException );
|
|
}
|
|
catch (...) {
|
|
// Add on the thread name, then note the exception
|
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
|
"caught unknown exception (thread: " + fName + ")"
|
|
);
|
|
fTestResult->addError( fObject, threadException );
|
|
}
|
|
|
|
// Release the semaphore we acquired earlier
|
|
release_sem(fThreadSem);
|
|
}
|
|
|
|
|
|
#endif
|