From dfd70c6069c6270ffbc85cf12d97fce6fd7e1010 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 11 Feb 2010 03:06:42 +0100 Subject: [PATCH] replace GThreads by the Lumiera thread wrapper --- src/backend/thread-wrapper.hpp | 16 +- tests/lib/sync-classlock-test.cpp | 123 +++++----- tests/lib/sync-locking-test.cpp | 312 ++++++++++++------------- tests/lib/sync-timedwait-test.cpp | 200 ++++++++-------- tests/lib/sync-waiting-test.cpp | 281 +++++++++++----------- tests/lib/thread-wrapper-join-test.cpp | 2 +- 6 files changed, 453 insertions(+), 481 deletions(-) diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index ab64939c9..2de5fea70 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -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 // baseclass { public: ThreadJoinable (Literal purpose, Operation const& operation, NoBugFlag logging_flag = &NOBUG_FLAG(thread)) - : Thread() { ThreadStartContext (thread_, operation, purpose, logging_flag, LUMIERA_THREAD_JOINABLE); diff --git a/tests/lib/sync-classlock-test.cpp b/tests/lib/sync-classlock-test.cpp index 2cfa271e5..585fff8d9 100644 --- a/tests/lib/sync-classlock-test.cpp +++ b/tests/lib/sync-classlock-test.cpp @@ -26,80 +26,75 @@ #include "lib/sync-classlock.hpp" -//#include - -//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 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 shared_lock_; - virtual void - run (Arg) - { - { - Probe objs[NUM_INSTANCES]; - - ASSERT (1 == objs[0].shared_lock_.use_count()); - } - - ClassLock get_class_lock; - ASSERT ( 1 == get_class_lock.use_count()); // embedded PerClassMonitor 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 get_class_lock; + ASSERT ( 1 == get_class_lock.use_count()); // embedded PerClassMonitor got created exactly once + } // and stays alive until static dtors are called.... + + }; + + + + /** Register this test class... */ + LAUNCHER (SyncClasslock_test, "unit common"); + + + +}} // namespace lib::test diff --git a/tests/lib/sync-locking-test.cpp b/tests/lib/sync-locking-test.cpp index d6586242c..9b4de03f9 100644 --- a/tests/lib/sync-locking-test.cpp +++ b/tests/lib/sync-locking-test.cpp @@ -25,186 +25,178 @@ #include "lib/error.hpp" #include "lib/sync.hpp" - -#include +#include "backend/thread-wrapper.hpp" #include +#include + +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 - { - 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); ijoin(); - } - - 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 { + 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=" << MAX_SUM << "\n"; - ourVictim.report(); + pause(); + cnt_[i] += step_; } } + + public: + Victim () + { + for (uint i=0; i=" << MAX_SUM << "\n"; + ourVictim.report(); + } + } + + }; + + + + /** Register this test class... */ + LAUNCHER (SyncLocking_test, "unit common"); + + + +}} // namespace lib::test diff --git a/tests/lib/sync-timedwait-test.cpp b/tests/lib/sync-timedwait-test.cpp index a9dcea3b1..b204689ab 100644 --- a/tests/lib/sync-timedwait-test.cpp +++ b/tests/lib/sync-timedwait-test.cpp @@ -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 + { - } // (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 - { - - 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 diff --git a/tests/lib/sync-waiting-test.cpp b/tests/lib/sync-waiting-test.cpp index 05fe56e7c..154b54979 100644 --- a/tests/lib/sync-waiting-test.cpp +++ b/tests/lib/sync-waiting-test.cpp @@ -24,165 +24,162 @@ #include "lib/test/run.hpp" #include "lib/error.hpp" +#include "backend/thread-wrapper.hpp" #include "lib/sync.hpp" -#include //////////////TODO temp solution, replace by backend! +#include +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 - { - 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 + { + 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 diff --git a/tests/lib/thread-wrapper-join-test.cpp b/tests/lib/thread-wrapper-join-test.cpp index 4e817301c..53836d5bf 100644 --- a/tests/lib/thread-wrapper-join-test.cpp +++ b/tests/lib/thread-wrapper-join-test.cpp @@ -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"); }