xref: /haiku/headers/tools/cppunit/ThreadManager.h (revision 1b89aa98ffe3057ab801dab67b9f5de6d1f933a5)
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
15 template <class TestClass, class ExpectedException>
16 class CPPUNIT_API BThreadManager {
17 public:
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 
getName()30 	std::string getName() const { return fName; }
31 
32 protected:
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 
45 template <class TestClass, class ExpectedException>
BThreadManager(std::string threadName,TestClass * object,ThreadMethod method,sem_id & threadSem)46 BThreadManager<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 
62 template <class TestClass, class ExpectedException>
~BThreadManager()63 BThreadManager<TestClass, ExpectedException>::~BThreadManager() {
64 	Stop();
65 }
66 
67 
68 template <class TestClass, class ExpectedException>
69 int32
WaitForThread()70 BThreadManager<TestClass, ExpectedException>::WaitForThread() {
71 	int32 result = 0;
72 	if (find_thread(NULL) != fID)
73 		wait_for_thread(fID, &result);
74 	return result;
75 }
76 
77 template <class TestClass, class ExpectedException>
78 int32
Stop()79 BThreadManager<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 
92 template <class TestClass, class ExpectedException>
93 bool
IsRunning(void)94 BThreadManager<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 
106 template <class TestClass, class ExpectedException>
107 status_t
LaunchThread(CppUnit::TestResult * result)108 BThreadManager<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 
130 template <class TestClass, class ExpectedException>
131 long
EntryFunction(BThreadManager<TestClass,ExpectedException> * manager)132 BThreadManager<TestClass, ExpectedException>::EntryFunction(BThreadManager<TestClass, ExpectedException> *manager) {
133 	manager->Run();
134 	return 0;
135 }
136 
137 template <class TestClass, class ExpectedException>
138 void
Run(void)139 BThreadManager<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