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:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
Reference in New Issue
Block a user