Fixed multithreaded NextSubTest() output problems

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@157 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Tyler Dauwalder
2002-07-12 20:30:45 +00:00
parent 40ea65d602
commit 0a99bdf489
6 changed files with 131 additions and 20 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef _beos_synchronization_object_h_
#define _beos_synchronization_object_h_
#ifndef _beos_locker_sync_object_h_
#define _beos_locker_sync_object_h_
#include <cppunit/SynchronizedObject.h>
#include <Locker.h>
+11 -3
View File
@@ -20,7 +20,7 @@ class BThreadManager {
public:
typedef void (TestClass::*ThreadMethod)();
BThreadManager(std::string threadName, TestClass *object, ThreadMethod method);
BThreadManager(std::string threadName, TestClass *object, ThreadMethod method, sem_id &threadSem);
~BThreadManager();
status_t LaunchThread(CppUnit::TestResult *result);
@@ -38,6 +38,7 @@ protected:
ThreadMethod fMethod;
thread_id fID;
CppUnit::TestResult *fTestResult;
sem_id &fThreadSem;
static long EntryFunction(BThreadManager<TestClass, ExpectedException>* manager);
void Run();
@@ -48,13 +49,15 @@ template <class TestClass, class ExpectedException>
BThreadManager<TestClass, ExpectedException>::BThreadManager(
std::string threadName,
TestClass *object,
ThreadMethod method
ThreadMethod method,
sem_id &threadSem
)
: fName(threadName)
, fObject(object)
, fMethod(method)
, fID(0)
, fTestResult(NULL)
, fThreadSem(threadSem)
{
}
@@ -118,6 +121,9 @@ BThreadManager<TestClass, ExpectedException>::LaunchThread(CppUnit::TestResult *
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;
@@ -185,7 +191,9 @@ BThreadManager<TestClass, ExpectedException>::Run(void) {
);
fTestResult->addError( fObject, threadException );
}
// Release the semaphore we acquired earlier
release_sem(fThreadSem);
}
+70 -6
View File
@@ -37,7 +37,10 @@ protected:
bool fOwnObject;
TestClass *fObject;
ThreadManagerMap fThreads;
ThreadManagerMap fThreads;
sem_id fThreadSem;
};
@@ -46,6 +49,7 @@ BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::stri
: TestCase(name)
, fOwnObject(true)
, fObject(new TestClass())
, fThreadSem(-1)
{
}
@@ -54,6 +58,7 @@ BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::stri
: TestCase(name)
, fOwnObject(false)
, fObject(&object)
, fThreadSem(-1)
{
}
@@ -62,6 +67,7 @@ BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::stri
: TestCase(name)
, fOwnObject(true)
, fObject(object)
, fThreadSem(-1)
{
}
@@ -73,13 +79,14 @@ BThreadedTestCaller<TestClass, ExpectedException>::~BThreadedTestCaller() {
delete it->second;
}
}
template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::addThread(std::string threadName, ThreadMethod method) {
if (fThreads.find(threadName) == fThreads.end()) {
// Unused name, go ahead and add
fThreads[threadName] = new BThreadManager<TestClass, ExpectedException>(threadName, fObject, method);
fThreads[threadName] = new BThreadManager<TestClass, ExpectedException>(threadName, fObject, method, fThreadSem);
} else {
// Duplicate name, throw an exception
throw CppUnit::Exception("BThreadedTestCaller::addThread() - Attempt to add thread under duplicated name ('"
@@ -91,6 +98,9 @@ template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *result) {
result->startTest(this);
if (fThreads.size() <= 0)
throw CppUnit::Exception("BThreadedTestCaller::run() -- No threads added to BThreadedTestCaller()");
try {
setUp();
@@ -101,10 +111,18 @@ BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *resu
// handles exceptions for its respective thread, so as not
// to disrupt the others.
try {
// Verify we have a valid object first.
if (!fObject)
throw CppUnit::Exception("BThreadedTestCaller::runTest() -- NULL fObject pointer");
// Create our thread semaphore. This semaphore is used to
// determine when all the threads have finished executing,
// while still allowing *this* thread to handle printing
// out NextSubTest() info (since other threads don't appear
// to be able to output text while the main thread is
// blocked; their output appears later...).
//
// Each thread will acquire the semaphore once when launched,
// thus the initial thread count is equal the number of threads.
fThreadSem = create_sem(fThreads.size(), "ThreadSem");
if (fThreadSem < B_OK)
throw CppUnit::Exception("BThreadedTestCaller::run() -- Error creating fThreadSem");
// Launch all the threads.
for (ThreadManagerMap::iterator i = fThreads.begin();
@@ -117,6 +135,44 @@ BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *resu
// printf("Launch(%s)\n", i->second->getName().c_str());
}
// Now we loop. Before you faint, there is a reason for this:
// Calls to NextSubTest() from other threads don't actually
// print anything while the main thread is blocked waiting
// for another thread. Thus, we have NextSubTest() add the
// information to be printed into a queue. The main thread
// (this code right here), blocks on a semaphore that it
// can only acquire after all the test threads have terminated.
// If it times out, it checks the NextSubTest() queue, prints
// any pending updates, and tries to acquire the semaphore
// again. When it finally manages to acquire it, all the
// test threads have terminated, and it's safe to clean up.
status_t err;
do {
// Try to acquire the semaphore
err = acquire_sem_etc(fThreadSem, fThreads.size(), B_RELATIVE_TIMEOUT, 500000);
// Empty the UpdateList
std::vector<std::string> &list = fObject->AcquireUpdateList();
for (std::vector<std::string>::iterator i = list.begin();
i != list.end();
i++)
{
printf("%s", (*i).c_str());
fflush(stdout);
}
list.clear();
fObject->ReleaseUpdateList();
} while (err != B_OK);
// If we get this far, we actually managed to acquire the semaphore,
// so we should release it now.
release_sem_etc(fThreadSem, fThreads.size(), 0);
/*
// Wait for them all to finish, then clean up
for (ThreadManagerMap::iterator i = fThreads.begin();
i != fThreads.end ();
@@ -128,6 +184,8 @@ BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *resu
// printf("done\n");
delete i->second;
}
*/
fThreads.clear();
} catch ( CppUnit::Exception &e ) {
@@ -171,6 +229,12 @@ BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *resu
template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::setUp() {
// Verify we have a valid object that's not currently in use first.
if (!fObject)
throw CppUnit::Exception("BThreadedTestCaller::runTest() -- NULL fObject pointer");
if (!fObject->RegisterForUse())
throw CppUnit::Exception("BThreadedTestCaller::runTest() -- Attempt to reuse ThreadedTestCase object already in use");
fObject->setUp();
}
+11 -1
View File
@@ -6,6 +6,7 @@
#include <TestCase.h>
#include <map>
#include <string>
#include <vector>
//! Base class for single threaded unit tests
class BThreadedTestCase : public BTestCase {
@@ -23,7 +24,14 @@ public:
//! Restores the current working directory to last directory saved by a call to SaveCWD().
void RestoreCWD(const char *alternate = NULL);
void InitThreadInfo(thread_id id, std::string threadName);
bool RegisterForUse();
void UnregisterForUse();
std::vector<std::string>& AcquireUpdateList();
void ReleaseUpdateList();
protected:
bool fInUse;
// friend class ThreadManager<BThreadedTestCase>;
std::string fProgressSeparator;
@@ -32,7 +40,9 @@ protected:
int32 subTestNum;
};
std::map<thread_id, ThreadSubTestInfo*> fNumberMap;
BLocker *fNumberMapLock;
std::vector<std::string> fUpdateList;
BLocker *fUpdateLock;
};
#endif // _beos_threaded_test_case_h_