1#ifndef _beos_thread_manager_h_
2#define _beos_thread_manager_h_
3
4#include <cppunit/Exception.h>
5#include <cppunit/TestResult.h>
6#include <OS.h>
7#include <signal.h>
8#include <string>
9
10// Pointer to a function that takes no parameters and
11// returns no result. All threads must be implemented
12// in a function of this type.
13
14// Helper class to handle thread management
15template <class TestClass, class ExpectedException>
16class CPPUNIT_API BThreadManager {
17public:
18	typedef void (TestClass::*ThreadMethod)();
19
20	BThreadManager(std::string threadName, TestClass *object, ThreadMethod method, sem_id &threadSem);
21	~BThreadManager();
22
23    status_t LaunchThread(CppUnit::TestResult *result);
24
25	// Thread management methods
26	int32 Stop();
27	int32 WaitForThread();
28	bool IsRunning();
29
30	std::string getName() const { return fName; }
31
32protected:
33	std::string fName;
34	TestClass *fObject;
35	ThreadMethod fMethod;
36	thread_id fID;
37	CppUnit::TestResult *fTestResult;
38	sem_id &fThreadSem;
39
40	static long EntryFunction(BThreadManager<TestClass, ExpectedException>* manager);
41	void Run();
42
43};
44
45template <class TestClass, class ExpectedException>
46BThreadManager<TestClass, ExpectedException>::BThreadManager(
47	std::string threadName,
48	TestClass *object,
49	ThreadMethod method,
50	sem_id &threadSem
51)
52	: fName(threadName)
53	, fObject(object)
54	, fMethod(method)
55	, fID(0)
56	, fTestResult(NULL)
57	, fThreadSem(threadSem)
58{
59}
60
61
62template <class TestClass, class ExpectedException>
63BThreadManager<TestClass, ExpectedException>::~BThreadManager() {
64	Stop();
65}
66
67
68template <class TestClass, class ExpectedException>
69int32
70BThreadManager<TestClass, ExpectedException>::WaitForThread() {
71	int32 result = 0;
72	if (find_thread(NULL) != fID)
73		wait_for_thread(fID, &result);
74	return result;
75}
76
77template <class TestClass, class ExpectedException>
78int32
79BThreadManager<TestClass, ExpectedException>::Stop() {
80	int32 result = 0;
81	if (find_thread(NULL) != fID) {
82		while (IsRunning()) {
83			kill(fID, SIGINT);
84			snooze(1000000);
85		}
86		result = WaitForThread();
87	}
88	return result;
89}
90
91
92template <class TestClass, class ExpectedException>
93bool
94BThreadManager<TestClass, ExpectedException>::IsRunning(void) {
95	if (fID != 0) {
96		thread_info info;
97		if (get_thread_info(fID, &info) == B_OK)
98			return true;
99		else
100			fID = 0;
101	}
102	return false;
103}
104
105
106template <class TestClass, class ExpectedException>
107status_t
108BThreadManager<TestClass, ExpectedException>::LaunchThread(CppUnit::TestResult *result) {
109	if (IsRunning())
110		return B_ALREADY_RUNNING;
111
112	fTestResult = result;
113	fID = spawn_thread((thread_entry)(BThreadManager::EntryFunction),
114		fName.c_str(), B_NORMAL_PRIORITY, this);
115
116	status_t err;
117	if (fID == B_NO_MORE_THREADS || fID == B_NO_MEMORY) {
118		err = fID;
119		fID = 0;
120	} else {
121		// Aquire the semaphore, then start the thread.
122		if (acquire_sem(fThreadSem) != B_OK)
123			throw CppUnit::Exception("BThreadManager::LaunchThread() -- Error acquiring thread semaphore");
124		err = resume_thread(fID);
125	}
126	return err;
127}
128
129
130template <class TestClass, class ExpectedException>
131long
132BThreadManager<TestClass, ExpectedException>::EntryFunction(BThreadManager<TestClass, ExpectedException> *manager) {
133	manager->Run();
134	return 0;
135}
136
137template <class TestClass, class ExpectedException>
138void
139BThreadManager<TestClass, ExpectedException>::Run(void) {
140	// These outer try/catch blocks handle unexpected exceptions.
141	// Said exceptions are caught and noted in the TestResult
142	// class, but not allowed to escape and disrupt the other
143	// threads that are assumingly running concurrently.
144	try {
145
146		// Our parent ThreadedTestCaller should check fObject to be non-NULL,
147		// but we'll do it here too just to be sure.
148		if (!fObject)
149			throw CppUnit::Exception("BThreadManager::Run() -- NULL fObject pointer");
150
151		// Before running, we need to add this thread's name to
152		// the object's id->(name,subtestnum) map.
153		fObject->InitThreadInfo(fID, fName);
154
155		// This inner try/catch block is for expected exceptions.
156		// If we get through it without an exception, we have to
157		// raise a different exception that makes note of the fact
158		// that the exception we were expecting didn't arrive. If
159		// no exception is expected, then nothing is done (see
160		// "cppunit/TestCaller.h" for detail on the classes used
161		// to handle this behaviour).
162		try {
163			(fObject->*fMethod)();
164	    } catch ( ExpectedException & ) {
165			return;
166		}
167	  	CppUnit::ExpectedExceptionTraits<ExpectedException>::expectedException();
168
169	} catch ( CppUnit::Exception &e ) {
170		// Add on the thread name, then note the exception
171        CppUnit::Exception *threadException = new CppUnit::Exception(
172        	std::string(e.what()) + " (thread: " + fName + ")",
173        	e.sourceLine()
174        );
175		fTestResult->addFailure( fObject, threadException );
176	}
177	catch ( std::exception &e ) {
178		// Add on the thread name, then note the exception
179        CppUnit::Exception *threadException = new CppUnit::Exception(
180        	std::string(e.what()) + " (thread: " + fName + ")"
181        );
182		fTestResult->addError( fObject, threadException );
183	}
184	catch (...) {
185		// Add on the thread name, then note the exception
186		CppUnit::Exception *threadException = new CppUnit::Exception(
187			"caught unknown exception (thread: " + fName + ")"
188		);
189		fTestResult->addError( fObject, threadException );
190	}
191
192	// Release the semaphore we acquired earlier
193	release_sem(fThreadSem);
194}
195
196
197#endif
198