root/headers/tools/cppunit/ThreadManager.h
#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