replace GThreads by the Lumiera thread wrapper
This commit is contained in:
parent
5386fe6fbc
commit
dfd70c6069
6 changed files with 453 additions and 481 deletions
|
|
@ -60,8 +60,7 @@ namespace backend {
|
|||
*
|
||||
*/
|
||||
class Thread
|
||||
: lib::BoolCheckable< Thread
|
||||
, boost::noncopyable> //////TODO: do we want Thread instances to be copyable?
|
||||
: boost::noncopyable //////TODO: do we want Thread instances to be copyable?
|
||||
{
|
||||
|
||||
protected:
|
||||
|
|
@ -136,15 +135,6 @@ namespace backend {
|
|||
ThreadStartContext (thread_, operation, purpose, logging_flag);
|
||||
}
|
||||
|
||||
/** automatic conversion: Thread instance can stand-in for a handle
|
||||
* @throws lumiera::error::State when thread isn't running */
|
||||
operator LumieraThread() const
|
||||
{
|
||||
if (!isValid())
|
||||
throw lumiera::error::State("thread not executing (anymore)");
|
||||
|
||||
return thread_;
|
||||
}
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
|
|
@ -178,12 +168,12 @@ namespace backend {
|
|||
* to join on the termination of this thread.
|
||||
*/
|
||||
class ThreadJoinable
|
||||
: public Thread
|
||||
: public lib::BoolCheckable<ThreadJoinable
|
||||
,Thread> // baseclass
|
||||
{
|
||||
public:
|
||||
ThreadJoinable (Literal purpose, Operation const& operation,
|
||||
NoBugFlag logging_flag = &NOBUG_FLAG(thread))
|
||||
: Thread()
|
||||
{
|
||||
ThreadStartContext (thread_, operation, purpose, logging_flag,
|
||||
LUMIERA_THREAD_JOINABLE);
|
||||
|
|
|
|||
|
|
@ -26,80 +26,75 @@
|
|||
|
||||
#include "lib/sync-classlock.hpp"
|
||||
|
||||
//#include <iostream>
|
||||
|
||||
//using std::cout;
|
||||
using test::Test;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
namespace test {
|
||||
|
||||
namespace { // private test classes and data...
|
||||
|
||||
namespace { // private test classes and data...
|
||||
|
||||
const uint NUM_INSTANCES = 20; ///< number of probe instances to create
|
||||
|
||||
|
||||
/**
|
||||
* Several instances of this probe class will be created.
|
||||
* Each of them acquires the shared lock; but anyway, just
|
||||
* by defining this class, the embedded Monitor got created.
|
||||
*/
|
||||
struct Probe
|
||||
{
|
||||
ClassLock<Probe> shared_lock_;
|
||||
|
||||
Probe() {}
|
||||
~Probe() {}
|
||||
};
|
||||
|
||||
|
||||
} // (End) test classes and data....
|
||||
const uint NUM_INSTANCES = 20; ///< number of probe instances to create
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* @test check proper handling of class (not instance)-based Monitor locks.
|
||||
* Because no instance is available in this case, a hidden storage for the
|
||||
* Monitor object needs to be provided in a way safe for use even in the
|
||||
* static startup/shutdown phase. This test validates the associated
|
||||
* refcounting and object creation works as expected. It does \em not
|
||||
* validate the locking functionality as such.
|
||||
*
|
||||
* @see sync.hpp
|
||||
/**
|
||||
* Several instances of this probe class will be created.
|
||||
* Each of them acquires the shared lock; but anyway, just
|
||||
* by defining this class, the embedded Monitor got created.
|
||||
*/
|
||||
class SyncClasslock_test : public Test
|
||||
struct Probe
|
||||
{
|
||||
ClassLock<Probe> shared_lock_;
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
{
|
||||
Probe objs[NUM_INSTANCES];
|
||||
|
||||
ASSERT (1 == objs[0].shared_lock_.use_count());
|
||||
}
|
||||
|
||||
ClassLock<Probe> get_class_lock;
|
||||
ASSERT ( 1 == get_class_lock.use_count()); // embedded PerClassMonitor<Probe> got created exactly once
|
||||
} // and stays alive until static dtors are called....
|
||||
|
||||
Probe() {}
|
||||
~Probe() {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncClasslock_test, "unit common");
|
||||
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace lib
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* @test check proper handling of class (not instance)-based Monitor locks.
|
||||
* Because no instance is available in this case, a hidden storage for the
|
||||
* Monitor object needs to be provided in a way safe for use even in the
|
||||
* static startup/shutdown phase. This test validates the associated
|
||||
* refcounting and object creation works as expected. It does \em not
|
||||
* validate the locking functionality as such.
|
||||
*
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncClasslock_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
{
|
||||
Probe objs[NUM_INSTANCES];
|
||||
|
||||
ASSERT (1 == objs[0].shared_lock_.use_count());
|
||||
}
|
||||
|
||||
ClassLock<Probe> get_class_lock;
|
||||
ASSERT ( 1 == get_class_lock.use_count()); // embedded PerClassMonitor<Probe> got created exactly once
|
||||
} // and stays alive until static dtors are called....
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncClasslock_test, "unit common");
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
|
|||
|
|
@ -25,186 +25,178 @@
|
|||
#include "lib/error.hpp"
|
||||
|
||||
#include "lib/sync.hpp"
|
||||
|
||||
#include <glibmm.h>
|
||||
#include "backend/thread-wrapper.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <tr1/functional>
|
||||
|
||||
using std::tr1::bind;
|
||||
|
||||
using std::cout;
|
||||
using test::Test;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
namespace test{
|
||||
|
||||
namespace { // private test classes and data...
|
||||
|
||||
const uint NUM_COUNTERS = 20; ///< number of independent counters to increment in parallel
|
||||
const uint NUM_THREADS = 10; ///< number of threads trying to increment these counters
|
||||
const uint MAX_PAUSE = 10000; ///< maximum delay implemented as empty counting loop
|
||||
const uint MAX_SUM = 1000; ///< trigger when to finish incrementing
|
||||
const uint MAX_INC = 10; ///< maximum increment on each step
|
||||
|
||||
|
||||
|
||||
class Victim
|
||||
: public Sync<RecursiveLock_NoWait>
|
||||
{
|
||||
volatile long cnt_[NUM_COUNTERS];
|
||||
volatile uint step_; ///< @note stored as instance variable
|
||||
|
||||
void
|
||||
pause ()
|
||||
{
|
||||
Lock guard (this); // note recursive lock
|
||||
|
||||
for ( uint i=0, lim=(rand() % MAX_PAUSE); i<lim; ++i)
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
incrementAll ()
|
||||
{
|
||||
for (uint i=0; i<NUM_COUNTERS; ++i)
|
||||
{
|
||||
pause();
|
||||
cnt_[i] += step_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
Victim ()
|
||||
{
|
||||
for (uint i=0; i<NUM_COUNTERS; ++i)
|
||||
cnt_[i] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
inc (uint newStep)
|
||||
{
|
||||
Lock guard (this);
|
||||
step_ = newStep;
|
||||
incrementAll();
|
||||
}
|
||||
|
||||
bool
|
||||
belowLimit ()
|
||||
{
|
||||
Lock guard (this);
|
||||
return cnt_[0] < MAX_SUM;
|
||||
}
|
||||
|
||||
bool
|
||||
checkAllEqual ()
|
||||
{
|
||||
for (uint i=1; i<NUM_COUNTERS; ++i)
|
||||
if (cnt_[i-1] != cnt_[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
report ()
|
||||
{
|
||||
for (uint i=0; i<NUM_COUNTERS; ++i)
|
||||
cout << "Counter-#" << i << " = " << cnt_[i] << "\n";
|
||||
}
|
||||
}
|
||||
ourVictim;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A Thread trying to increment all victim counters in sync...
|
||||
*/
|
||||
class HavocThread
|
||||
{
|
||||
Glib::Thread * thread_;
|
||||
|
||||
void
|
||||
doIt ()
|
||||
{
|
||||
while (ourVictim.belowLimit())
|
||||
ourVictim.inc (rand() % MAX_INC);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
HavocThread ()
|
||||
: thread_(0)
|
||||
{ }
|
||||
|
||||
~HavocThread ()
|
||||
{
|
||||
if (thread_)
|
||||
thread_->join();
|
||||
}
|
||||
|
||||
void
|
||||
start ()
|
||||
{
|
||||
thread_ = Glib::Thread::create(sigc::mem_fun(*this, &HavocThread::doIt), true);
|
||||
ASSERT (thread_);
|
||||
}
|
||||
};
|
||||
|
||||
} // (End) test classes and data....
|
||||
namespace { // private test classes and data...
|
||||
|
||||
const uint NUM_COUNTERS = 20; ///< number of independent counters to increment in parallel
|
||||
const uint NUM_THREADS = 10; ///< number of threads trying to increment these counters
|
||||
const uint MAX_PAUSE = 10000; ///< maximum delay implemented as empty counting loop
|
||||
const uint MAX_SUM = 1000; ///< trigger when to finish incrementing
|
||||
const uint MAX_INC = 10; ///< maximum increment on each step
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* @test create multiple threads, all concurrently trying to increment
|
||||
* a number of counters with random steps and random pauses. Without
|
||||
* locking, the likely result will be differing counters.
|
||||
* But because the class Victim uses an object level monitor to
|
||||
* guard the mutations, the state should remain consistent.
|
||||
*
|
||||
* @see SyncWaiting_test condition based wait/notify
|
||||
* @see SyncClasslock_test locking a type, not an instance
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncLocking_test : public Test
|
||||
class Victim
|
||||
: public Sync<RecursiveLock_NoWait>
|
||||
{
|
||||
volatile long cnt_[NUM_COUNTERS];
|
||||
volatile uint step_; ///< @note stored as instance variable
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
void
|
||||
pause ()
|
||||
{
|
||||
if (!Glib::thread_supported())
|
||||
Glib::thread_init();
|
||||
Lock guard (this); // note recursive lock
|
||||
|
||||
REQUIRE (ourVictim.checkAllEqual());
|
||||
{
|
||||
HavocThread threads[NUM_THREADS];
|
||||
for (uint i=0; i<NUM_THREADS; ++i)
|
||||
threads[i].start();
|
||||
}
|
||||
// all finished and joined here...
|
||||
|
||||
if (!ourVictim.checkAllEqual())
|
||||
for ( uint i=0, lim=(rand() % MAX_PAUSE); i<lim; ++i)
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
incrementAll ()
|
||||
{
|
||||
for (uint i=0; i<NUM_COUNTERS; ++i)
|
||||
{
|
||||
cout << "Thread locking is broken; internal state got messed up\n"
|
||||
"NOTE: all counters should be equal and >=" << MAX_SUM << "\n";
|
||||
ourVictim.report();
|
||||
pause();
|
||||
cnt_[i] += step_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
Victim ()
|
||||
{
|
||||
for (uint i=0; i<NUM_COUNTERS; ++i)
|
||||
cnt_[i] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
inc (uint newStep)
|
||||
{
|
||||
Lock guard (this);
|
||||
step_ = newStep;
|
||||
incrementAll();
|
||||
}
|
||||
|
||||
bool
|
||||
belowLimit ()
|
||||
{
|
||||
Lock guard (this);
|
||||
return cnt_[0] < MAX_SUM;
|
||||
}
|
||||
|
||||
bool
|
||||
checkAllEqual ()
|
||||
{
|
||||
for (uint i=1; i<NUM_COUNTERS; ++i)
|
||||
if (cnt_[i-1] != cnt_[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
report ()
|
||||
{
|
||||
for (uint i=0; i<NUM_COUNTERS; ++i)
|
||||
cout << "Counter-#" << i << " = " << cnt_[i] << "\n";
|
||||
}
|
||||
}
|
||||
ourVictim;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A Thread trying to increment all victim counters in sync...
|
||||
*/
|
||||
class HavocThread
|
||||
{
|
||||
backend::ThreadJoinable thread_;
|
||||
|
||||
void
|
||||
doIt ()
|
||||
{
|
||||
while (ourVictim.belowLimit())
|
||||
ourVictim.inc (rand() % MAX_INC);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
HavocThread ()
|
||||
: thread_("HavocThread"
|
||||
, bind (&HavocThread::doIt, this)
|
||||
)
|
||||
{
|
||||
ASSERT (thread_);
|
||||
}
|
||||
|
||||
~HavocThread ()
|
||||
{
|
||||
if (thread_)
|
||||
thread_.join();
|
||||
}
|
||||
};
|
||||
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* @test create multiple threads, all concurrently trying to increment
|
||||
* a number of counters with random steps and random pauses. Without
|
||||
* locking, the likely result will be differing counters.
|
||||
* But because the class Victim uses an object level monitor to
|
||||
* guard the mutations, the state should remain consistent.
|
||||
*
|
||||
* @see SyncWaiting_test condition based wait/notify
|
||||
* @see SyncClasslock_test locking a type, not an instance
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncLocking_test : public Test
|
||||
{
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncLocking_test, "unit common");
|
||||
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace lib
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
REQUIRE (ourVictim.checkAllEqual());
|
||||
{
|
||||
HavocThread threads[NUM_THREADS] SIDEEFFECT;
|
||||
}
|
||||
// all finished and joined here...
|
||||
|
||||
if (!ourVictim.checkAllEqual())
|
||||
{
|
||||
cout << "Thread locking is broken; internal state got messed up\n"
|
||||
"NOTE: all counters should be equal and >=" << MAX_SUM << "\n";
|
||||
ourVictim.report();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncLocking_test, "unit common");
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
|
|||
|
|
@ -33,108 +33,106 @@ using test::Test;
|
|||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
namespace test{
|
||||
|
||||
namespace { // private test classes and data...
|
||||
namespace { // private test classes and data...
|
||||
|
||||
const uint WAIT_mSec = 200; ///< milliseconds to wait before timeout
|
||||
const uint WAIT_mSec = 200; ///< milliseconds to wait before timeout
|
||||
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* @test timeout feature on condition wait as provided by pthread and accessible
|
||||
* via the object monitor based locking/waiting mechanism. Without creating
|
||||
* multiple threads, we engage into a blocking wait, which aborts due to
|
||||
* setting a timeout. (Note it is discouraged to use the timed wait feature;
|
||||
* when possible, you should prefer relying on the Lumiera scheduler)
|
||||
*
|
||||
* @see SyncWaiting_test
|
||||
* @see sync::Timeout
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncTimedwait_test
|
||||
: public Test,
|
||||
Sync<RecursiveLock_Waitable>
|
||||
{
|
||||
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* @test timeout feature on condition wait as provided by pthread and accessible
|
||||
* via the object monitor based locking/waiting mechanism. Without creating
|
||||
* multiple threads, we engage into a blocking wait, which aborts due to
|
||||
* setting a timeout. (Note it is discouraged to use the timed wait feature;
|
||||
* when possible, you should prefer relying on the Lumiera scheduler)
|
||||
*
|
||||
* @see SyncWaiting_test
|
||||
* @see sync::Timeout
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncTimedwait_test
|
||||
: public Test,
|
||||
Sync<RecursiveLock_Waitable>
|
||||
{
|
||||
|
||||
friend class Lock; // allows inheriting privately from Sync
|
||||
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
checkTimeoutStruct();
|
||||
|
||||
Lock block(this, &SyncTimedwait_test::neverHappens);
|
||||
|
||||
cout << "back from LaLaLand, alive and thriving!\n";
|
||||
ASSERT (block.isTimedWait());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
neverHappens() ///< the "condition test" used for waiting....
|
||||
{
|
||||
Lock currentLock(this); // get the Lock recursively
|
||||
if (!currentLock.isTimedWait()) // right from within the condition test:
|
||||
currentLock.setTimeout(WAIT_mSec); // switch waiting mode to timed wait and set timeout
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
checkTimeoutStruct()
|
||||
{
|
||||
sync::Timeout tout;
|
||||
|
||||
ASSERT (!tout);
|
||||
ASSERT (0 == tout.tv_sec);
|
||||
ASSERT (0 == tout.tv_nsec);
|
||||
|
||||
tout.setOffset (0);
|
||||
ASSERT (!tout);
|
||||
ASSERT (0 == tout.tv_sec);
|
||||
ASSERT (0 == tout.tv_nsec);
|
||||
|
||||
timespec ref;
|
||||
clock_gettime(CLOCK_REALTIME, &ref);
|
||||
tout.setOffset (1);
|
||||
ASSERT (tout);
|
||||
ASSERT (0 < tout.tv_sec);
|
||||
ASSERT (ref.tv_sec <= tout.tv_sec);
|
||||
ASSERT (ref.tv_nsec <= 1000000 + tout.tv_nsec || ref.tv_nsec > 1000000000-100000);
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &ref);
|
||||
tout.setOffset (1000);
|
||||
ASSERT (tout);
|
||||
if (ref.tv_nsec!=0) // should have gotten an overflow to the seconds part
|
||||
{
|
||||
ASSERT (ref.tv_sec <= 2 + tout.tv_sec );
|
||||
ASSERT ((ref.tv_nsec + 1000000 * 999) % 1000000000
|
||||
<= tout.tv_nsec);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
friend class Lock; // allows inheriting privately from Sync
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncTimedwait_test, "unit common");
|
||||
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace lib
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
checkTimeoutStruct();
|
||||
|
||||
Lock block(this, &SyncTimedwait_test::neverHappens);
|
||||
|
||||
cout << "back from LaLaLand, alive and thriving!\n";
|
||||
ASSERT (block.isTimedWait());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
neverHappens() ///< the "condition test" used for waiting....
|
||||
{
|
||||
Lock currentLock(this); // get the Lock recursively
|
||||
if (!currentLock.isTimedWait()) // right from within the condition test:
|
||||
currentLock.setTimeout(WAIT_mSec); // switch waiting mode to timed wait and set timeout
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
checkTimeoutStruct()
|
||||
{
|
||||
sync::Timeout tout;
|
||||
|
||||
ASSERT (!tout);
|
||||
ASSERT (0 == tout.tv_sec);
|
||||
ASSERT (0 == tout.tv_nsec);
|
||||
|
||||
tout.setOffset (0);
|
||||
ASSERT (!tout);
|
||||
ASSERT (0 == tout.tv_sec);
|
||||
ASSERT (0 == tout.tv_nsec);
|
||||
|
||||
timespec ref;
|
||||
clock_gettime(CLOCK_REALTIME, &ref);
|
||||
tout.setOffset (1);
|
||||
ASSERT (tout);
|
||||
ASSERT (0 < tout.tv_sec);
|
||||
ASSERT (ref.tv_sec <= tout.tv_sec);
|
||||
ASSERT (ref.tv_nsec <= 1000000 + tout.tv_nsec || ref.tv_nsec > 1000000000-100000);
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &ref);
|
||||
tout.setOffset (1000);
|
||||
ASSERT (tout);
|
||||
if (ref.tv_nsec!=0) // should have gotten an overflow to the seconds part
|
||||
{
|
||||
ASSERT (ref.tv_sec <= 2 + tout.tv_sec );
|
||||
ASSERT ((ref.tv_nsec + 1000000 * 999) % 1000000000
|
||||
<= tout.tv_nsec);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncTimedwait_test, "unit common");
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
|
|||
|
|
@ -24,165 +24,162 @@
|
|||
#include "lib/test/run.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include "backend/thread-wrapper.hpp"
|
||||
#include "lib/sync.hpp"
|
||||
|
||||
#include <glibmm.h> //////////////TODO temp solution, replace by backend!
|
||||
#include <tr1/functional>
|
||||
|
||||
using std::tr1::bind;
|
||||
using test::Test;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
namespace test{
|
||||
|
||||
namespace { // private test classes and data...
|
||||
|
||||
|
||||
/** Interface defining the basic interaction pattern for this test */
|
||||
class Token
|
||||
{
|
||||
public:
|
||||
|
||||
/** blocking concurrent operation */
|
||||
virtual void getIt() =0;
|
||||
|
||||
/** start the notification chain */
|
||||
virtual void provide (uint val) =0;
|
||||
|
||||
/** harvesting the result...*/
|
||||
uint result () { return sum_; }
|
||||
|
||||
|
||||
protected:
|
||||
volatile uint sum_, input_;
|
||||
|
||||
virtual ~Token() {}
|
||||
|
||||
Token() : sum_(0), input_(0) {}
|
||||
};
|
||||
|
||||
|
||||
/** demonstrates how to wait on a simple boolean flag */
|
||||
class SyncOnBool
|
||||
: public Token,
|
||||
public Sync<NonrecursiveLock_Waitable>
|
||||
{
|
||||
protected:
|
||||
volatile bool got_new_data_;
|
||||
|
||||
public:
|
||||
SyncOnBool() : got_new_data_ (false) {}
|
||||
|
||||
void getIt()
|
||||
{
|
||||
Lock(this).wait (got_new_data_);
|
||||
sum_ += input_;
|
||||
}
|
||||
|
||||
void provide (uint val)
|
||||
{
|
||||
Lock sync(this);
|
||||
input_ = val;
|
||||
got_new_data_ = true;
|
||||
sync.notifyAll();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** this variant demonstrates how to wait on an condition
|
||||
* defined in terms of a (bool) member function
|
||||
*/
|
||||
class SyncOnMemberPredicate
|
||||
: public SyncOnBool
|
||||
{
|
||||
bool checkTheFlag() { return this->got_new_data_; }
|
||||
|
||||
public:
|
||||
void getIt()
|
||||
{
|
||||
Lock guard(this, &SyncOnMemberPredicate::checkTheFlag);
|
||||
sum_ += input_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // (End) test classes and data....
|
||||
namespace { // private test classes and data...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* @test concurrent waiting and notification, implemented via object monitor.
|
||||
* This test covers the second part of the monitor pattern, which builds upon
|
||||
* the locking part an additionally uses an embedded condition. We provide
|
||||
* several pre-configured ways of specifying the condition to wait upon.
|
||||
* - check a boolean flag
|
||||
* - evaluate a member function as predicate
|
||||
*
|
||||
* @see SyncLocking_test
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncWaiting_test : public Test
|
||||
/** Interface defining the basic interaction pattern for this test */
|
||||
class Token
|
||||
{
|
||||
public:
|
||||
|
||||
/** blocking concurrent operation */
|
||||
virtual void getIt() =0;
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
/** start the notification chain */
|
||||
virtual void provide (uint val) =0;
|
||||
|
||||
/** harvesting the result...*/
|
||||
uint result () { return sum_; }
|
||||
|
||||
|
||||
protected:
|
||||
volatile uint sum_, input_;
|
||||
|
||||
virtual ~Token() {}
|
||||
|
||||
Token() : sum_(0), input_(0) {}
|
||||
};
|
||||
|
||||
|
||||
/** demonstrates how to wait on a simple boolean flag */
|
||||
class SyncOnBool
|
||||
: public Token,
|
||||
public Sync<NonrecursiveLock_Waitable>
|
||||
{
|
||||
protected:
|
||||
volatile bool got_new_data_;
|
||||
|
||||
public:
|
||||
SyncOnBool() : got_new_data_ (false) {}
|
||||
|
||||
void getIt()
|
||||
{
|
||||
if (!Glib::thread_supported())
|
||||
Glib::thread_init();
|
||||
|
||||
SyncOnBool use_sync_var;
|
||||
waitPingPong (use_sync_var);
|
||||
|
||||
SyncOnMemberPredicate use_member_pred;
|
||||
waitPingPong (use_member_pred);
|
||||
Lock(this).wait (got_new_data_);
|
||||
sum_ += input_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper actually performing the test:
|
||||
* creates two threads and let them block and wait until a start value is given.
|
||||
* When awakened, each thread should add the start value to a common sum field.
|
||||
* @param tok object containing the monitor and condition to be tested.
|
||||
*/
|
||||
void
|
||||
waitPingPong (Token& tok)
|
||||
void provide (uint val)
|
||||
{
|
||||
Glib::Thread *ping, *pong;
|
||||
|
||||
ping = Glib::Thread::create(sigc::mem_fun(tok, &Token::getIt), true);
|
||||
pong = Glib::Thread::create(sigc::mem_fun(tok, &Token::getIt), true);
|
||||
|
||||
ASSERT (ping);
|
||||
ASSERT (pong);
|
||||
ASSERT (0 == tok.result());
|
||||
|
||||
usleep (100000); // if the threads don't block correctly, they've missed their chance by now...
|
||||
|
||||
// kick off the notification cascade...
|
||||
uint val = (rand() % 1000);
|
||||
tok.provide (val);
|
||||
|
||||
// wait for the two Threads to finish their handshake
|
||||
pong->join();
|
||||
ping->join();
|
||||
|
||||
ASSERT (2*val == tok.result());
|
||||
Lock sync(this);
|
||||
input_ = val;
|
||||
got_new_data_ = true;
|
||||
sync.notifyAll();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** this variant demonstrates how to wait on an condition
|
||||
* defined in terms of a (bool) member function
|
||||
*/
|
||||
class SyncOnMemberPredicate
|
||||
: public SyncOnBool
|
||||
{
|
||||
bool checkTheFlag() { return this->got_new_data_; }
|
||||
|
||||
public:
|
||||
void getIt()
|
||||
{
|
||||
Lock await(this, &SyncOnMemberPredicate::checkTheFlag);
|
||||
sum_ += input_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* @test concurrent waiting and notification, implemented via object monitor.
|
||||
* This test covers the second part of the monitor pattern, which builds upon
|
||||
* the locking part, additionally using an embedded condition. We provide
|
||||
* several pre-configured ways of specifying the condition to wait upon.
|
||||
* - check a boolean flag
|
||||
* - evaluate a member function as predicate
|
||||
*
|
||||
* @see SyncLocking_test
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncWaiting_test : public Test
|
||||
{
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncWaiting_test, "unit common");
|
||||
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace lib
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
SyncOnBool use_sync_var;
|
||||
waitPingPong (use_sync_var);
|
||||
|
||||
SyncOnMemberPredicate use_member_pred;
|
||||
waitPingPong (use_member_pred);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper actually performing the test:
|
||||
* creates two threads and let them block and wait until a start value is given.
|
||||
* When awakened, each thread should add the start value to a common sum field.
|
||||
* @param tok object containing the monitor and condition to be tested.
|
||||
*/
|
||||
void
|
||||
waitPingPong (Token& tok)
|
||||
{
|
||||
typedef backend::ThreadJoinable Thread;
|
||||
|
||||
Thread ping ("SyncWaiting ping", bind (&Token::getIt, &tok));
|
||||
Thread pong ("SyncWaiting pong", bind (&Token::getIt, &tok));
|
||||
|
||||
ASSERT (ping);
|
||||
ASSERT (pong);
|
||||
ASSERT (0 == tok.result());
|
||||
|
||||
usleep (100000); // if the threads don't block correctly, they've missed their chance by now...
|
||||
|
||||
// kick off the notification cascade...
|
||||
uint val = (rand() % 1000);
|
||||
tok.provide (val);
|
||||
|
||||
// wait for the two Threads to finish their handshake
|
||||
pong.join();
|
||||
ping.join();
|
||||
|
||||
ASSERT (2*val == tok.result());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncWaiting_test, "unit common");
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace test {
|
|||
using lumiera::error::LUMIERA_ERROR_LOGIC;
|
||||
namespace {
|
||||
|
||||
const uint DESTRUCTION_CODE = 23;
|
||||
const int DESTRUCTION_CODE = 23;
|
||||
|
||||
LUMIERA_ERROR_DEFINE(SPECIAL, "grandiose exception");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue