[svn r136] MAJOR UNSTABLE UPDATE!!!

Initial commit after moving to Tango instead of Phobos.

Lots of bugfixes...

This build is not suitable for most things.
This commit is contained in:
Tomas Lindquist Olsen
2008-01-11 17:57:40 +01:00
parent bc08c6fcb1
commit b15b3484c8
524 changed files with 270705 additions and 175 deletions

View File

@@ -0,0 +1,138 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.Barrier;
private import tango.core.sync.Mutex;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.io.Stdout;
private import tango.text.convert.Integer;
debug (barrier)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
/**
* Example program for the tango.core.sync.Barrier module.
*/
void main(char[][] args)
{
const uint MaxThreadCount = 100;
const uint LoopsPerThread = 10000;
debug (barrier)
{
Logger log = Log.getLogger("barrier");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Barrier test");
}
Barrier barrier = new Barrier(MaxThreadCount);
Mutex mutex = new Mutex();
uint count = 0;
uint correctCount = 0;
void barrierTestThread()
{
debug (barrier)
{
Logger log = Log.getLogger("barrier." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
for (uint i; i < LoopsPerThread; ++i)
{
// 'count' is a resource shared by multiple threads, so we must
// acquire the mutex before modifying it.
synchronized (mutex)
{
// debug (barrier)
// log.trace("Acquired mutex");
count++;
// debug (barrier)
// log.trace("Releasing mutex");
}
}
// We wait for all the threads to finish counting.
debug (barrier)
log.trace("Waiting on barrier");
barrier.wait();
debug (barrier)
log.trace("Barrier was opened");
// We make sure that all the threads exited the barrier after
// *all* of them had finished counting.
synchronized (mutex)
{
// debug (barrier)
// log.trace("Acquired mutex");
if (count == MaxThreadCount * LoopsPerThread)
{
++correctCount;
}
// debug (barrier)
// log.trace("Releasing mutex");
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in Barrier test thread {0}:\n{1}\n",
Thread.getThis().name, e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in Barrier test thread {0}:\n{1}\n",
Thread.getThis().name, e.toString());
}
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; ++i)
{
thread = new Thread(&barrierTestThread);
thread.name = "thread-" ~ format(tmp, i);
group.add(thread);
debug (barrier)
log.trace("Created thread " ~ thread.name);
thread.start();
}
debug (barrier)
log.trace("Waiting for threads to finish");
group.joinAll();
if (count == MaxThreadCount * LoopsPerThread)
{
debug (barrier)
log.info("The Barrier test was successful");
}
else
{
debug (barrier)
{
log.error("The Barrier is not working properly: the counter has an incorrect value");
assert(false);
}
else
{
assert(false, "The Barrier is not working properly: the counter has an incorrect value");
}
}
}

View File

@@ -0,0 +1,307 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.Condition;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.text.convert.Integer;
private import tango.io.Stdout;
debug (condition)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
void main(char[][] args)
{
debug (condition)
{
Logger log = Log.getLogger("condition");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Condition test");
}
testNotifyOne();
testNotifyAll();
}
/**
* Test for Condition.notify().
*/
void testNotifyOne()
{
debug (condition)
{
Logger log = Log.getLogger("condition.notify-one");
}
scope Mutex mutex = new Mutex();
scope Condition cond = new Condition(mutex);
int waiting = 0;
Thread thread;
void notifyOneTestThread()
{
debug (condition)
{
Logger log = Log.getLogger("condition.notify-one." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
scope(exit)
{
debug (condition)
log.trace("Releasing mutex");
}
waiting++;
while (waiting != 2)
{
debug (condition)
log.trace("Waiting on condition variable");
cond.wait();
}
debug (condition)
log.trace("Condition variable was signaled");
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
debug (condition)
log.trace("Exiting thread");
}
thread = new Thread(&notifyOneTestThread);
thread.name = "thread-1";
debug (condition)
log.trace("Created thread " ~ thread.name);
thread.start();
try
{
// Poor man's barrier: wait until the other thread is waiting.
while (true)
{
synchronized (mutex)
{
if (waiting != 1)
{
Thread.yield();
}
else
{
break;
}
}
}
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
waiting++;
debug (condition)
log.trace("Notifying test thread");
cond.notify();
debug (condition)
log.trace("Releasing mutex");
}
thread.join();
if (waiting == 2)
{
debug (condition)
log.info("The Condition notification test to one thread was successful");
}
else
{
debug (condition)
{
log.error("The condition variable notification to one thread is not working");
assert(false);
}
else
{
assert(false, "The condition variable notification to one thread is not working");
}
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in main thread:\n{0}", e.toString());
}
}
/**
* Test for Condition.notifyAll().
*/
void testNotifyAll()
{
const uint MaxThreadCount = 10;
debug (condition)
{
Logger log = Log.getLogger("condition.notify-all");
}
scope Mutex mutex = new Mutex();
scope Condition cond = new Condition(mutex);
int waiting = 0;
/**
* This thread waits for a notification from the main thread.
*/
void notifyAllTestThread()
{
debug (condition)
{
Logger log = Log.getLogger("condition.notify-all." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
waiting++;
while (waiting != MaxThreadCount + 1)
{
debug (condition)
log.trace("Waiting on condition variable");
cond.wait();
}
debug (condition)
log.trace("Condition variable was signaled");
debug (condition)
log.trace("Releasing mutex");
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
debug (condition)
log.trace("Exiting thread");
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; ++i)
{
thread = new Thread(&notifyAllTestThread);
thread.name = "thread-" ~ format(tmp, i);
group.add(thread);
debug (condition)
log.trace("Created thread " ~ thread.name);
thread.start();
}
try
{
// Poor man's barrier: wait until all the threads are waiting.
while (true)
{
synchronized (mutex)
{
if (waiting != MaxThreadCount)
{
Thread.yield();
}
else
{
break;
}
}
}
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
waiting++;
debug (condition)
log.trace("Notifying all threads");
cond.notifyAll();
debug (condition)
log.trace("Releasing mutex");
}
debug (condition)
log.trace("Waiting for threads to finish");
group.joinAll();
if (waiting == MaxThreadCount + 1)
{
debug (condition)
log.info("The Condition notification test to many threads was successful");
}
else
{
debug (condition)
{
log.error("The condition variable notification to many threads is not working");
assert(false);
}
else
{
assert(false, "The condition variable notification to many threads is not working");
}
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in main thread:\n{0}", e.toString());
}
}

View File

@@ -0,0 +1,248 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.Mutex;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.io.Stdout;
private import tango.text.convert.Integer;
debug (mutex)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
/**
* Example program for the tango.core.sync.Mutex module.
*/
void main(char[][] args)
{
debug (mutex)
{
Logger log = Log.getLogger("mutex");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Mutex test");
}
testNonRecursive();
testLocking();
testRecursive();
}
/**
* Test that non-recursive mutexes actually do what they're supposed to do.
*
* Remarks:
* Windows only supports recursive mutexes.
*/
void testNonRecursive()
{
version (Posix)
{
debug (mutex)
{
Logger log = Log.getLogger("mutex.non-recursive");
}
Mutex mutex = new Mutex(Mutex.Type.NonRecursive);
bool couldLock;
try
{
mutex.lock();
debug (mutex)
log.trace("Acquired mutex");
couldLock = mutex.tryLock();
if (couldLock)
{
debug (mutex)
{
log.trace("Re-acquired mutex");
log.trace("Releasing mutex");
}
mutex.unlock();
}
else
{
debug (mutex)
log.trace("Re-acquiring the mutex failed");
}
debug (mutex)
log.trace("Releasing mutex");
mutex.unlock();
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught when testing non-recursive mutexes:\n{0}\n", e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught when testing non-recursive mutexes:\n{0}\n", e.toString());
}
if (!couldLock)
{
debug (mutex)
log.info("The non-recursive Mutex test was successful");
}
else
{
debug (mutex)
{
log.error("Non-recursive mutexes are not working: "
"Mutex.tryAcquire() did not fail on an already acquired mutex");
assert(false);
}
else
{
assert(false, "Non-recursive mutexes are not working: "
"Mutex.tryAcquire() did not fail on an already acquired mutex");
}
}
}
}
/**
* Create several threads that acquire and release a mutex several times.
*/
void testLocking()
{
const uint MaxThreadCount = 10;
const uint LoopsPerThread = 1000;
debug (mutex)
{
Logger log = Log.getLogger("mutex.locking");
}
Mutex mutex = new Mutex();
uint lockCount = 0;
void mutexLockingThread()
{
try
{
for (uint i; i < LoopsPerThread; i++)
{
synchronized (mutex)
{
lockCount++;
}
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught inside mutex testing thread:\n{0}\n", e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught inside mutex testing thread:\n{0}\n", e.toString());
}
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; i++)
{
thread = new Thread(&mutexLockingThread);
thread.name = "thread-" ~ format(tmp, i);
debug (mutex)
log.trace("Created thread " ~ thread.name);
thread.start();
group.add(thread);
}
debug (mutex)
log.trace("Waiting for threads to finish");
group.joinAll();
if (lockCount == MaxThreadCount * LoopsPerThread)
{
debug (mutex)
log.info("The Mutex locking test was successful");
}
else
{
debug (mutex)
{
log.error("Mutex locking is not working properly: "
"the number of times the mutex was acquired is incorrect");
assert(false);
}
else
{
assert(false,"Mutex locking is not working properly: "
"the number of times the mutex was acquired is incorrect");
}
}
}
/**
* Test that recursive mutexes actually do what they're supposed to do.
*/
void testRecursive()
{
const uint LoopsPerThread = 1000;
debug (mutex)
{
Logger log = Log.getLogger("mutex.recursive");
}
Mutex mutex = new Mutex;
uint lockCount = 0;
try
{
for (uint i = 0; i < LoopsPerThread; i++)
{
mutex.lock();
lockCount++;
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in recursive mutex test:\n{0}\n", e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in recursive mutex test:\n{0}\n", e.toString());
}
for (uint i = 0; i < lockCount; i++)
{
mutex.unlock();
}
if (lockCount == LoopsPerThread)
{
debug (mutex)
log.info("The recursive Mutex test was successful");
}
else
{
debug (mutex)
{
log.error("Recursive mutexes are not working: "
"the number of times the mutex was acquired is incorrect");
assert(false);
}
else
{
assert(false, "Recursive mutexes are not working: "
"the number of times the mutex was acquired is incorrect");
}
}
}

View File

@@ -0,0 +1,145 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.ReadWriteMutex;
private import tango.core.sync.Mutex;
private import tango.core.Thread;
private import tango.text.convert.Integer;
debug (readwritemutex)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
/**
* Example program for the tango.core.sync.ReadWriteMutex module.
*/
void main(char[][] args)
{
const uint ReaderThreads = 100;
const uint WriterThreads = 20;
const uint LoopsPerReader = 10000;
const uint LoopsPerWriter = 1000;
const uint CounterIncrement = 3;
debug (readwritemutex)
{
Logger log = Log.getLogger("readwritemutex");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("ReadWriteMutex test");
}
ReadWriteMutex rwlock = new ReadWriteMutex();
Mutex mutex = new Mutex();
uint readCount = 0;
uint passed = 0;
uint failed = 0;
void mutexReaderThread()
{
debug (readwritemutex)
{
Logger log = Log.getLogger("readwritemutex." ~ Thread.getThis().name());
log.trace("Starting reader thread");
}
for (uint i = 0; i < LoopsPerReader; ++i)
{
// All the reader threads acquire the mutex for reading and when they are
// all done
synchronized (rwlock.reader)
{
for (uint j = 0; j < CounterIncrement; ++j)
{
synchronized (mutex)
{
++readCount;
}
}
}
}
}
void mutexWriterThread()
{
debug (readwritemutex)
{
Logger log = Log.getLogger("readwritemutex." ~ Thread.getThis().name());
log.trace("Starting writer thread");
}
for (uint i = 0; i < LoopsPerWriter; ++i)
{
synchronized (rwlock.writer)
{
synchronized (mutex)
{
if (readCount % 3 == 0)
{
++passed;
}
}
}
}
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < ReaderThreads; ++i)
{
thread = new Thread(&mutexReaderThread);
thread.name = "reader-" ~ format(tmp, i);
debug (readwritemutex)
log.trace("Created reader thread " ~ thread.name);
thread.start();
group.add(thread);
}
for (uint i = 0; i < WriterThreads; ++i)
{
thread = new Thread(&mutexWriterThread);
thread.name = "writer-" ~ format(tmp, i);
debug (readwritemutex)
log.trace("Created writer thread " ~ thread.name);
thread.start();
group.add(thread);
}
debug (readwritemutex)
log.trace("Waiting for threads to finish");
group.joinAll();
if (passed == WriterThreads * LoopsPerWriter)
{
debug (readwritemutex)
log.info("The ReadWriteMutex test was successful");
}
else
{
debug (readwritemutex)
{
log.error("The ReadWriteMutex is not working properly: the counter has an incorrect value");
assert(false);
}
else
{
assert(false, "The ReadWriteMutex is not working properly: the counter has an incorrect value");
}
}
}

View File

@@ -0,0 +1,340 @@
/*******************************************************************************
copyright: Copyright (c) 2007 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
module semaphore;
private import tango.core.sync.Semaphore;
private import tango.core.sync.Mutex;
private import tango.core.Exception;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.io.Console;
private import tango.text.stream.LineIterator;
private import tango.text.convert.Integer;
private import tango.sys.Process;
debug (semaphore)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
const char[] SemaphoreName = "TestProcessSemaphore";
/**
* Example program for the tango.core.sync.Barrier module.
*/
int main(char[][] args)
{
if (args.length == 1)
{
debug (semaphore)
{
Logger log = Log.getLogger("semaphore");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Semaphore test");
}
testSemaphore();
testProcessSemaphore(args[0]);
return 0;
}
else
{
return testSecondProcessSemaphore();
}
}
/**
* Test for single-process (multi-threaded) semaphores.
*/
void testSemaphore()
{
const uint MaxThreadCount = 10;
// Semaphore used in the tests. Start it "locked" (i.e., its initial
// count is 0).
Semaphore sem = new Semaphore(MaxThreadCount - 1);
Mutex mutex = new Mutex();
uint count = 0;
bool passed = false;
void semaphoreTestThread()
{
debug (semaphore)
{
Logger log = Log.getLogger("semaphore.single." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
uint threadNumber;
// 'count' is a resource shared by multiple threads, so we must
// acquire the mutex before modifying it.
synchronized (mutex)
{
// debug (semaphore)
// log.trace("Acquired mutex");
threadNumber = ++count;
// debug (semaphore)
// log.trace("Releasing mutex");
}
// We wait for all the threads to finish counting.
if (threadNumber < MaxThreadCount)
{
sem.wait();
debug (semaphore)
log.trace("Acquired semaphore");
while (true)
{
synchronized (mutex)
{
if (count >= MaxThreadCount + 1)
break;
}
Thread.yield();
}
debug (semaphore)
log.trace("Releasing semaphore");
sem.notify();
}
else
{
passed = !sem.tryWait();
if (passed)
{
debug (semaphore)
log.trace("Tried to acquire the semaphore too many times and failed: OK");
}
else
{
debug (semaphore)
log.error("Tried to acquire the semaphore too may times and succeeded: FAILED");
debug (semaphore)
log.trace("Releasing semaphore");
sem.notify();
}
synchronized (mutex)
{
count++;
}
}
}
catch (SyncException e)
{
Cerr("Sync exception caught in Semaphore test thread " ~ Thread.getThis().name ~
":\n" ~ e.toString()).newline;
}
catch (Exception e)
{
Cerr("Unexpected exception caught in Semaphore test thread " ~ Thread.getThis().name ~
":\n" ~ e.toString()).newline;
}
}
debug (semaphore)
{
Logger log = Log.getLogger("semaphore.single");
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; ++i)
{
thread = new Thread(&semaphoreTestThread);
thread.name = "thread-" ~ tango.text.convert.Integer.format(tmp, i);
group.add(thread);
debug (semaphore)
log.trace("Created thread " ~ thread.name);
thread.start();
}
debug (semaphore)
log.trace("Waiting for threads to finish");
group.joinAll();
if (passed)
{
debug (semaphore)
log.info("The Semaphore test was successful");
}
else
{
debug (semaphore)
{
log.error("The Semaphore is not working properly: it allowed "
"to be acquired more than it should have done");
assert(false);
}
else
{
assert(false, "The Semaphore is not working properly: it allowed "
"to be acquired more than it should have done");
}
}
}
/**
* Test for multi-process semaphores: this test works by creating a copy of
* this process that tries to acquire the ProcessSemaphore that was created
* in this function. If everything works as expected, the attempt should fail,
* as the count of the semaphore is set to 1.
*/
void testProcessSemaphore(char[] programName)
{
/+
bool success = false;
debug (semaphore)
{
Logger log = Log.getLogger("semaphore.multi");
Logger childLog = Log.getLogger("semaphore.multi.child");
log.info("ProcessSemaphore test");
}
try
{
scope ProcessSemaphore sem = new ProcessSemaphore(SemaphoreName, 1);
Process proc = new Process(programName, "2");
debug (semaphore)
log.trace("Created ProcessSemaphore('" ~ SemaphoreName ~ "')'");
sem.wait();
debug (semaphore)
log.trace("Acquired semaphore in main process");
debug (semaphore)
log.trace("Executing child test process: " ~ proc.toString());
proc.execute();
debug (semaphore)
{
foreach (line; new LineIterator!(char)(proc.stdout))
{
childLog.trace(line);
}
}
foreach (line; new LineIterator!(char)(proc.stderr))
{
Cerr(line).newline;
}
debug (semaphore)
log.trace("Waiting for child process to finish");
auto result = proc.wait();
success = (result.reason == Process.Result.Exit && result.status == 2);
debug (semaphore)
log.trace("Releasing semaphore in main process");
sem.notify();
}
catch (SyncException e)
{
Cerr("Sync exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
}
catch (ProcessException e)
{
Cerr("Process exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
}
catch (Exception e)
{
Cerr("Unexpected exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
}
if (success)
{
debug (semaphore)
log.info("The ProcessSemaphore test was successful");
}
else
{
debug (semaphore)
{
log.error("The multi-process semaphore is not working");
assert(false);
}
else
{
assert(false, "The multi-process semaphore is not working");
}
}
+/
}
/**
* Test for multi-process semaphores (second process).
*/
int testSecondProcessSemaphore()
{
int rc = 0;
/+
debug (semaphore)
{
Cout("Starting child process\n");
}
try
{
scope ProcessSemaphore sem = new ProcessSemaphore(SemaphoreName);
bool success;
success = !sem.tryAcquire();
if (success)
{
debug (semaphore)
Cout("Tried to acquire semaphore in child process and failed: OK\n");
rc = 2;
}
else
{
debug (semaphore)
{
Cout("Acquired semaphore in child process: this should not have happened\n");
Cout("Releasing semaphore in child process\n");
}
sem.notify();
rc = 1;
}
}
catch (SyncException e)
{
Cerr("Sync exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
}
catch (ProcessException e)
{
Cerr("Process exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
}
catch (Exception e)
{
Cerr("Unexpected exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
}
debug (semaphore)
Cout("Leaving child process\n");
+/
return rc;
}