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 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> 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> 63 BThreadManager<TestClass, ExpectedException>::~BThreadManager() { 64 Stop(); 65 } 66 67 68 template <class TestClass, class ExpectedException> 69 int32 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 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 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 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 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 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