From d5710ffc54aa788374335ff3d82e9d830c49b9b1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 14 Dec 2008 04:34:40 +0100 Subject: [PATCH 01/25] WIP: draft a test to check monitor object locking --- tests/40components.tests | 5 + tests/common/multithread-locking-test.cpp | 211 ++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 tests/common/multithread-locking-test.cpp diff --git a/tests/40components.tests b/tests/40components.tests index 4ff311a4d..c73e24eb1 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -220,6 +220,11 @@ return: 0 END +PLANNED "Multithread Locking by Monitor" MultithreadLocking_test < [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ] out: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ] diff --git a/tests/common/multithread-locking-test.cpp b/tests/common/multithread-locking-test.cpp new file mode 100644 index 000000000..d03fe5c28 --- /dev/null +++ b/tests/common/multithread-locking-test.cpp @@ -0,0 +1,211 @@ +/* + MultithreadLocking(Test) - check the monitor object based locking + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "common/test/run.hpp" +//#include "common/util.hpp" +#include "common/error.hpp" + +#include "common/multithread.hpp" + +#include + +//#include +//#include +#include + +//using boost::lexical_cast; +//using boost::format; +//using util::isnil; +//using std::string; +using std::cout; +using test::Test; + + +namespace lib { + 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 + { + long cnt_[NUM_COUNTERS]; + 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.... + + + + + + + + + + + /********************************************************************** + * @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 multithread.hpp + */ + class MultithreadLocking_test : public Test + { + + virtual void run(Arg arg) + { + if (!Glib::thread_supported()) + Glib::thread_init(); + + REQUIRE (ourVictim.checkAllEqual()); + { + HavocThread threads[NUM_THREADS]; + for (uint i=0; i=" << MAX_SUM << "\n"; + ourVictim.report(); + } + } + + }; + + + + /** Register this test class... */ + LAUNCHER (MultithreadLocking_test, "unit common"); + + + + } // namespace test + +} // namespace lumiera From 93b57b3bc828b916faea02edb82a43ff994cf992 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 14 Dec 2008 04:35:36 +0100 Subject: [PATCH 02/25] SCons: add glib-- and gthread-2.0 to the build environment of Lumiera core --- SConstruct | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SConstruct b/SConstruct index 52493aa4b..e38511152 100644 --- a/SConstruct +++ b/SConstruct @@ -255,6 +255,9 @@ def configurePlatform(env): if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): problems.append('Unable to configure Lib glib--, exiting.') + if not conf.CheckPkgConfig('gthread-2.0', '2.16'): + problems.append('Need gthread support lib for glib-- based thread handling.') + if not conf.CheckPkgConfig('cairomm-1.0', 0.6): problems.append('Unable to configure Cairo--, exiting.') @@ -312,6 +315,8 @@ def defineBuildTargets(env, artifacts): setup sub-environments with special build options if necessary. We use a custom function to declare a whole tree of srcfiles. """ + env.mergeConf(['glibmm-2.4','gthread-2.0']) + # use PCH to speed up building # env['GCH'] = ( env.PrecompiledHeader('$SRCDIR/pre.hpp') # + env.PrecompiledHeader('$SRCDIR/pre_a.hpp') From ee4c910ed410cf13a62befe670277a42ced4c26f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 15 Dec 2008 03:13:14 +0100 Subject: [PATCH 03/25] qualify with volatile (fix) --- tests/common/multithread-locking-test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common/multithread-locking-test.cpp b/tests/common/multithread-locking-test.cpp index d03fe5c28..465929e55 100644 --- a/tests/common/multithread-locking-test.cpp +++ b/tests/common/multithread-locking-test.cpp @@ -56,8 +56,8 @@ namespace lib { class Victim { - long cnt_[NUM_COUNTERS]; - uint step_; ///< @note stored as instance variable + volatile long cnt_[NUM_COUNTERS]; + volatile uint step_; ///< @note stored as instance variable void pause () From d1e385f3f43beacff896d90434501267012ac3d1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 15 Dec 2008 04:14:15 +0100 Subject: [PATCH 04/25] rename class Thread into Concurrency it's not a thread abstraction, but a locking helper --- src/lib/Makefile.am | 2 +- src/lib/allocationcluster.cpp | 12 ++++++------ src/lib/allocationcluster.hpp | 6 +++--- src/lib/{multithread.hpp => concurrency.hpp} | 7 +++---- src/lib/singletonpolicies.hpp | 4 ++-- src/lib/visitordispatcher.hpp | 8 ++++---- src/proc/assetmanager.cpp | 6 +++--- src/proc/mobject/session/defsregistry.hpp | 6 +++--- tests/40components.tests | 10 +++++----- ...locking-test.cpp => concurrency-locking-test.cpp} | 10 +++++----- 10 files changed, 35 insertions(+), 36 deletions(-) rename src/lib/{multithread.hpp => concurrency.hpp} (93%) rename tests/common/{multithread-locking-test.cpp => concurrency-locking-test.cpp} (95%) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 4608feedb..6460a0f88 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -68,7 +68,7 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/visitor.hpp \ $(liblumiera_la_srcdir)/visitordispatcher.hpp \ $(liblumiera_la_srcdir)/visitorpolicies.hpp \ - $(liblumiera_la_srcdir)/multithread.hpp \ + $(liblumiera_la_srcdir)/concurrency.hpp \ $(liblumiera_la_srcdir)/p.hpp \ $(liblumiera_la_srcdir)/query.hpp \ $(liblumiera_la_srcdir)/singletonfactory.hpp \ diff --git a/src/lib/allocationcluster.cpp b/src/lib/allocationcluster.cpp index e30561952..20ae8ae0a 100644 --- a/src/lib/allocationcluster.cpp +++ b/src/lib/allocationcluster.cpp @@ -81,7 +81,7 @@ namespace lib { void AllocationCluster::MemoryManager::reset (TypeInfo info) { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; if (0 < mem_.size()) purge(); type_ = info; @@ -96,7 +96,7 @@ namespace lib { void AllocationCluster::MemoryManager::purge() { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; REQUIRE (type_.killIt, "we need a deleter function"); REQUIRE (0 < type_.allocSize, "allocation size unknown"); @@ -120,7 +120,7 @@ namespace lib { inline void* AllocationCluster::MemoryManager::allocate() { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; REQUIRE (0 < type_.allocSize); REQUIRE (top_ <= mem_.size()); @@ -140,7 +140,7 @@ namespace lib { inline void AllocationCluster::MemoryManager::commit (void* pendingAlloc) { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; REQUIRE (pendingAlloc); ASSERT (top_ < mem_.size()); @@ -175,7 +175,7 @@ namespace lib { { try { - Thread::Lock guard SIDEEFFECT + Concurrency::Lock guard SIDEEFFECT TRACE (memory, "shutting down AllocationCluster"); for (size_t i = typeHandlers_.size(); 0 < i; --i) @@ -214,7 +214,7 @@ namespace lib { ASSERT (0 < slot); { - Thread::Lock guard SIDEEFFECT; /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? + Concurrency::Lock guard SIDEEFFECT; /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? if (slot > typeHandlers_.size()) typeHandlers_.resize(slot); diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index 0e6a61a8d..ada15c501 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -51,8 +51,8 @@ #include #include -#include "lib/multithread.hpp" #include "include/error.hpp" +#include "lib/concurrency.hpp" #include "lib/scopedholder.hpp" #include "lib/scopedholdertransfer.hpp" @@ -60,7 +60,7 @@ namespace lib { using boost::scoped_ptr; - using lumiera::Thread; + using lumiera::Concurrency; /** * A pile of objects sharing common allocation and lifecycle. @@ -223,7 +223,7 @@ namespace lib { static TypeInfo setup() { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; if (!id_) id_= ++maxTypeIDs; diff --git a/src/lib/multithread.hpp b/src/lib/concurrency.hpp similarity index 93% rename from src/lib/multithread.hpp rename to src/lib/concurrency.hpp index 2d3b4b31f..f13f44711 100644 --- a/src/lib/multithread.hpp +++ b/src/lib/concurrency.hpp @@ -1,5 +1,5 @@ /* - MULTITHREAD.hpp - generic interface for multithreading primitives + CONCURRENCY.hpp - generic interface for multithreading primitives Copyright (C) Lumiera.org 2008, Christian Thaeter @@ -30,8 +30,7 @@ #include "lib/util.hpp" -namespace lumiera - { +namespace lumiera { /** * Interface/Policy for managing parallelism issues. @@ -40,7 +39,7 @@ namespace lumiera * * @todo actually implement this policy using the Lumiera databackend. */ - struct Thread + struct Concurrency { template class Lock diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index 572467264..25c174a58 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -34,7 +34,7 @@ This code is heavily inspired by #ifndef LUMIERA_SINGLETONPOLICIES_H #define LUMIERA_SINGLETONPOLICIES_H -#include "lib/multithread.hpp" +#include "lib/concurrency.hpp" #include "include/error.hpp" #include @@ -139,7 +139,7 @@ namespace lumiera struct Multithreaded { typedef volatile S VolatileType; - typedef lumiera::Thread::Lock Lock; + typedef lumiera::Concurrency::Lock Lock; }; diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 857047aa5..5598b243c 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -26,9 +26,9 @@ #define LUMIERA_VISITORDISPATCHER_H #include "include/error.hpp" -#include "lib/util.hpp" +#include "lib/concurrency.hpp" #include "lib/singleton.hpp" -#include "lib/multithread.hpp" +#include "lib/util.hpp" #include @@ -62,7 +62,7 @@ namespace lumiera static void generateID (size_t& id) { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; if (!id) id = ++lastRegisteredID; } @@ -138,7 +138,7 @@ namespace lumiera void accomodate (size_t index) { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; if (index > table_.size()) table_.resize (index); // performance bottleneck?? TODO: measure the real impact! } diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 3c17421a9..9251ceaab 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -24,7 +24,7 @@ #include "proc/assetmanager.hpp" #include "proc/asset/db.hpp" -#include "lib/multithread.hpp" +#include "lib/concurrency.hpp" #include "lib/util.hpp" #include @@ -39,7 +39,7 @@ using boost::bind; using util::for_each; using lumiera::Singleton; -using lumiera::Thread; +using lumiera::Concurrency; namespace asset @@ -116,7 +116,7 @@ namespace asset TODO ("check validity of Ident Category"); ID asset_id (getID (idi)); - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index da79f40fa..89054de66 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -40,7 +40,7 @@ #define MOBJECT_SESSION_DEFSREGISTRY_H -#include "lib/multithread.hpp" +#include "lib/concurrency.hpp" #include "lib/query.hpp" #include "lib/util.hpp" #include "lib/p.hpp" @@ -59,7 +59,7 @@ namespace mobject { using lumiera::P; using lumiera::Query; - using lumiera::Thread; + using lumiera::Concurrency; using std::tr1::weak_ptr; using std::string; @@ -163,7 +163,7 @@ namespace mobject static void createSlot (Table& table) { - Thread::Lock guard SIDEEFFECT; + Concurrency::Lock guard SIDEEFFECT; if (!index) index = ++maxSlots; if (index > table.size()) diff --git a/tests/40components.tests b/tests/40components.tests index c73e24eb1..c1bf07b0c 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -158,6 +158,11 @@ return: 0 END +PLANNED "Multithread Locking by Monitor" ConcurrencyLocking_test < [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ] out: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ] diff --git a/tests/common/multithread-locking-test.cpp b/tests/common/concurrency-locking-test.cpp similarity index 95% rename from tests/common/multithread-locking-test.cpp rename to tests/common/concurrency-locking-test.cpp index 465929e55..c55aac0bf 100644 --- a/tests/common/multithread-locking-test.cpp +++ b/tests/common/concurrency-locking-test.cpp @@ -1,5 +1,5 @@ /* - MultithreadLocking(Test) - check the monitor object based locking + ConcurrencyLocking(Test) - check the monitor object based locking Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -25,7 +25,7 @@ //#include "common/util.hpp" #include "common/error.hpp" -#include "common/multithread.hpp" +#include "common/concurrency.hpp" #include @@ -171,9 +171,9 @@ namespace lib { * But because the class Victim uses an object level monitor to * guard the mutations, the state should remain consistent. * - * @see multithread.hpp + * @see concurrency.hpp */ - class MultithreadLocking_test : public Test + class ConcurrencyLocking_test : public Test { virtual void run(Arg arg) @@ -202,7 +202,7 @@ namespace lib { /** Register this test class... */ - LAUNCHER (MultithreadLocking_test, "unit common"); + LAUNCHER (ConcurrencyLocking_test, "unit common"); From 112cd475b7bc2cfbc1c3146097f687e88f339657 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 15 Dec 2008 05:05:12 +0100 Subject: [PATCH 05/25] WIP draft the basic pattern --- src/lib/concurrency.hpp | 61 ++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/lib/concurrency.hpp b/src/lib/concurrency.hpp index f13f44711..87ebfa45b 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/concurrency.hpp @@ -1,5 +1,5 @@ /* - CONCURRENCY.hpp - generic interface for multithreading primitives + CONCURRENCY.hpp - generic helper for object based locking and synchronisation Copyright (C) Lumiera.org 2008, Christian Thaeter @@ -21,10 +21,21 @@ */ +/** @file concurrency.hpp + ** Collection of helpers and wrappers to support dealing with concurrency issues. + ** Actually, everything is implemented either by the Lumiera backend, or directly + ** by pthread. The purpose is to support and automate the most common use cases + ** in object oriented style. + ** + ** @see mutex.h + ** @see concurrency-locking-test.cpp + ** @see asset::AssetManager::reg() usage example + ** @see subsystemrunner.hpp usage example + */ -#ifndef LUMIERA_MULTITHREAD_H -#define LUMIERA_MULTITHREAD_H +#ifndef LUMIERA_CONCURRENCY_H +#define LUMIERA_CONCURRENCY_H #include "include/nobugcfg.h" #include "lib/util.hpp" @@ -33,24 +44,54 @@ namespace lumiera { /** - * Interface/Policy for managing parallelism issues. - * Basically everything is forwarded to the corresponding backend functions, - * because managing threads and locking belongs to the Lumiera backend layer. + * Facility for monitor object based locking. + * To be attached either on a per class base or per object base. + * Typically, the client class will inherit from this template (but it + * is possible to use it stand-alone, if inheriting isn't an option). + * The interface for clients to access the functionality is the embedded + * Lock template, which should be instantiated as an automatic variable + * within the scope to be protected. * - * @todo actually implement this policy using the Lumiera databackend. + * @todo actually implement this facility using the Lumiera backend. */ struct Concurrency { + struct Monitor + { + void acquireLock() { TODO ("acquire Thread Lock"); } + void releaseLock() { TODO ("release Thread Lock"); } + }; + + template + static Monitor& getMonitor(X* forThis); + template class Lock { + Monitor& mon_; public: - Lock() { TODO ("aquire Thread Lock for Class"); } - Lock(X*) { TODO ("aquire Thread Lock for Instance"); } - ~Lock() { TODO ("release Thread Lock"); } + Lock(X* it=0) + : mon_(getMonitor (it)) + { + mon_.acquireLock(); + } + + ~Lock() + { + mon_.releaseLock(); + } }; }; + + + template + Concurrency::Monitor& + Concurrency::getMonitor(X* forThis) + { + UNIMPLEMENTED ("get moni"); + } + } // namespace lumiera #endif From 2512f04f3f874d7549a21db1c470c7492aa4f243 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 15 Dec 2008 05:20:30 +0100 Subject: [PATCH 06/25] WIP yet another namespace change. Now refer it from the test --- src/lib/allocationcluster.hpp | 1 - src/lib/concurrency.hpp | 6 +++--- src/lib/singletonpolicies.hpp | 2 +- src/lib/visitordispatcher.hpp | 8 ++++---- src/proc/assetmanager.cpp | 2 +- src/proc/mobject/session/defsregistry.hpp | 9 ++++----- tests/common/concurrency-locking-test.cpp | 7 ++++--- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index ada15c501..0e1e85fdd 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -60,7 +60,6 @@ namespace lib { using boost::scoped_ptr; - using lumiera::Concurrency; /** * A pile of objects sharing common allocation and lifecycle. diff --git a/src/lib/concurrency.hpp b/src/lib/concurrency.hpp index 87ebfa45b..e00f1bd5d 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/concurrency.hpp @@ -34,14 +34,14 @@ */ -#ifndef LUMIERA_CONCURRENCY_H -#define LUMIERA_CONCURRENCY_H +#ifndef LIB_CONCURRENCY_H +#define LIB_CONCURRENCY_H #include "include/nobugcfg.h" #include "lib/util.hpp" -namespace lumiera { +namespace lib { /** * Facility for monitor object based locking. diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index 25c174a58..e00be62b6 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -139,7 +139,7 @@ namespace lumiera struct Multithreaded { typedef volatile S VolatileType; - typedef lumiera::Concurrency::Lock Lock; + typedef lib::Concurrency::Lock Lock; }; diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 5598b243c..46db635b0 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -33,10 +33,10 @@ #include -namespace lumiera - { - namespace visitor - { +namespace lumiera { + namespace visitor { + + using lib::Concurrency; template class Tag; diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 9251ceaab..7c4b6da06 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -39,7 +39,7 @@ using boost::bind; using util::for_each; using lumiera::Singleton; -using lumiera::Concurrency; +using lib::Concurrency; namespace asset diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 89054de66..3920ed143 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -53,13 +53,12 @@ #include -namespace mobject - { - namespace session - { +namespace mobject { + namespace session { + using lumiera::P; using lumiera::Query; - using lumiera::Concurrency; + using lib::Concurrency; using std::tr1::weak_ptr; using std::string; diff --git a/tests/common/concurrency-locking-test.cpp b/tests/common/concurrency-locking-test.cpp index c55aac0bf..7729e4dc4 100644 --- a/tests/common/concurrency-locking-test.cpp +++ b/tests/common/concurrency-locking-test.cpp @@ -55,6 +55,7 @@ namespace lib { class Victim + : Concurrency { volatile long cnt_[NUM_COUNTERS]; volatile uint step_; ///< @note stored as instance variable @@ -62,7 +63,7 @@ namespace lib { void pause () { - //Lock guard (*this); // note recursive lock + Lock guard (this); // note recursive lock for ( uint i=0, lim=(rand() % MAX_PAUSE); i guard (this); step_ = newStep; incrementAll(); } @@ -95,7 +96,7 @@ namespace lib { bool belowLimit () { - //Lock guard (*this); + Lock guard (this); return cnt_[0] < MAX_SUM; } From c4df935113c33f00394065c833f3e65d3a9cae36 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 15 Dec 2008 06:18:38 +0100 Subject: [PATCH 07/25] add draft impl for Monitor storage; dummy impl running todo: actually do any locking, improve handling of the forThis parameter --- src/lib/concurrency.hpp | 50 +++++++++++++++++++---- tests/common/concurrency-locking-test.cpp | 3 +- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/lib/concurrency.hpp b/src/lib/concurrency.hpp index e00f1bd5d..369afa01a 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/concurrency.hpp @@ -40,8 +40,15 @@ #include "include/nobugcfg.h" #include "lib/util.hpp" +#include + +#include ///////////////////////////TODO +using std::cerr; + namespace lib { + + using boost::scoped_ptr; /** * Facility for monitor object based locking. @@ -58,20 +65,38 @@ namespace lib { { struct Monitor { - void acquireLock() { TODO ("acquire Thread Lock"); } - void releaseLock() { TODO ("release Thread Lock"); } + Monitor() { TODO ("maybe create mutex struct here?"); } + ~Monitor() { TODO ("destroy mutex, assert it isn't locked!"); } /////TODO: Probably need to unlock it silently in case of a class-level monitor? + + void acquireLock() { cerr << "acquire Thread Lock\n"; } + void releaseLock() { cerr << "release Thread Lock\n"; } }; + Monitor objectMonitor_; + + /////////////////////////////////////////////////////////////////////////TODO: any better idea for where to put the template parameter? so we can get rid of it for the typical usage scenario? + /////////////////////////////////////////////////////////////////////////TODO: better solution for the storage? Implement the per-this locking without subclassing! + /////////////////////////////////////////////////////////////////////////TODO: factor out the recursive/non-recursive mutex case as policy... + template - static Monitor& getMonitor(X* forThis); + static inline Monitor& getMonitor(); + + static inline Monitor& getMonitor(Concurrency* forThis); + template class Lock { Monitor& mon_; public: - Lock(X* it=0) - : mon_(getMonitor (it)) + Lock(X* it) + : mon_(getMonitor(it)) + { + mon_.acquireLock(); + } + + Lock() + : mon_(getMonitor()) { mon_.acquireLock(); } @@ -85,11 +110,22 @@ namespace lib { + Concurrency::Monitor& + Concurrency::getMonitor(Concurrency* forThis) + { + REQUIRE (forThis); + return forThis->objectMonitor_; + } + template Concurrency::Monitor& - Concurrency::getMonitor(X* forThis) + Concurrency::getMonitor() { - UNIMPLEMENTED ("get moni"); + //TODO: guard this by a Mutex? consider double checked locking? (but then class Monitor needs to be volatile, thus is it worth the effort?) + + static scoped_ptr classMonitor_ (0); + if (!classMonitor_) classMonitor_.reset (new Monitor ()); + return *classMonitor_; } diff --git a/tests/common/concurrency-locking-test.cpp b/tests/common/concurrency-locking-test.cpp index 7729e4dc4..e3b0bbbe6 100644 --- a/tests/common/concurrency-locking-test.cpp +++ b/tests/common/concurrency-locking-test.cpp @@ -55,7 +55,7 @@ namespace lib { class Victim - : Concurrency + : public Concurrency { volatile long cnt_[NUM_COUNTERS]; volatile uint step_; ///< @note stored as instance variable @@ -64,6 +64,7 @@ namespace lib { pause () { Lock guard (this); // note recursive lock + for ( uint i=0, lim=(rand() % MAX_PAUSE); i Date: Tue, 16 Dec 2008 05:19:31 +0100 Subject: [PATCH 08/25] better invocation, get rid of the template parameter --- src/lib/allocationcluster.cpp | 12 ++++++------ src/lib/allocationcluster.hpp | 2 +- src/lib/concurrency.hpp | 18 ++++++++++++------ src/lib/singletonpolicies.hpp | 2 +- src/lib/visitordispatcher.hpp | 4 ++-- src/proc/assetmanager.cpp | 2 +- src/proc/mobject/session/defsregistry.hpp | 2 +- tests/common/concurrency-locking-test.cpp | 6 +++--- 8 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/lib/allocationcluster.cpp b/src/lib/allocationcluster.cpp index 20ae8ae0a..06982df51 100644 --- a/src/lib/allocationcluster.cpp +++ b/src/lib/allocationcluster.cpp @@ -81,7 +81,7 @@ namespace lib { void AllocationCluster::MemoryManager::reset (TypeInfo info) { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); if (0 < mem_.size()) purge(); type_ = info; @@ -96,7 +96,7 @@ namespace lib { void AllocationCluster::MemoryManager::purge() { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); REQUIRE (type_.killIt, "we need a deleter function"); REQUIRE (0 < type_.allocSize, "allocation size unknown"); @@ -120,7 +120,7 @@ namespace lib { inline void* AllocationCluster::MemoryManager::allocate() { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); REQUIRE (0 < type_.allocSize); REQUIRE (top_ <= mem_.size()); @@ -140,7 +140,7 @@ namespace lib { inline void AllocationCluster::MemoryManager::commit (void* pendingAlloc) { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); REQUIRE (pendingAlloc); ASSERT (top_ < mem_.size()); @@ -175,7 +175,7 @@ namespace lib { { try { - Concurrency::Lock guard SIDEEFFECT + Concurrency::ClassLock guard(); TRACE (memory, "shutting down AllocationCluster"); for (size_t i = typeHandlers_.size(); 0 < i; --i) @@ -214,7 +214,7 @@ namespace lib { ASSERT (0 < slot); { - Concurrency::Lock guard SIDEEFFECT; /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? + Concurrency::ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? if (slot > typeHandlers_.size()) typeHandlers_.resize(slot); diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index 0e1e85fdd..dd7ce8959 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -222,7 +222,7 @@ namespace lib { static TypeInfo setup() { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); if (!id_) id_= ++maxTypeIDs; diff --git a/src/lib/concurrency.hpp b/src/lib/concurrency.hpp index 369afa01a..fc40a4edf 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/concurrency.hpp @@ -74,7 +74,6 @@ namespace lib { Monitor objectMonitor_; - /////////////////////////////////////////////////////////////////////////TODO: any better idea for where to put the template parameter? so we can get rid of it for the typical usage scenario? /////////////////////////////////////////////////////////////////////////TODO: better solution for the storage? Implement the per-this locking without subclassing! /////////////////////////////////////////////////////////////////////////TODO: factor out the recursive/non-recursive mutex case as policy... @@ -84,19 +83,17 @@ namespace lib { static inline Monitor& getMonitor(Concurrency* forThis); - template class Lock { Monitor& mon_; public: - Lock(X* it) - : mon_(getMonitor(it)) + template + Lock(X* it) : mon_(getMonitor(it)) { mon_.acquireLock(); } - Lock() - : mon_(getMonitor()) + Lock(Monitor& m) : mon_(m) { mon_.acquireLock(); } @@ -106,8 +103,17 @@ namespace lib { mon_.releaseLock(); } }; + + template + struct ClassLock : Lock + { + ClassLock() : Lock (getMonitor()) {} + }; + }; + + Concurrency::Monitor& diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index e00be62b6..afbb87253 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -139,7 +139,7 @@ namespace lumiera struct Multithreaded { typedef volatile S VolatileType; - typedef lib::Concurrency::Lock Lock; + typedef lib::Concurrency::ClassLock Lock; }; diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 46db635b0..de56445b9 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -62,7 +62,7 @@ namespace lumiera { static void generateID (size_t& id) { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); if (!id) id = ++lastRegisteredID; } @@ -138,7 +138,7 @@ namespace lumiera { void accomodate (size_t index) { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); if (index > table_.size()) table_.resize (index); // performance bottleneck?? TODO: measure the real impact! } diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 7c4b6da06..fb2623153 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -116,7 +116,7 @@ namespace asset TODO ("check validity of Ident Category"); ID asset_id (getID (idi)); - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 3920ed143..8a3fcdfd5 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -162,7 +162,7 @@ namespace mobject { static void createSlot (Table& table) { - Concurrency::Lock guard SIDEEFFECT; + Concurrency::ClassLock guard(); if (!index) index = ++maxSlots; if (index > table.size()) diff --git a/tests/common/concurrency-locking-test.cpp b/tests/common/concurrency-locking-test.cpp index e3b0bbbe6..2bc7065b1 100644 --- a/tests/common/concurrency-locking-test.cpp +++ b/tests/common/concurrency-locking-test.cpp @@ -63,7 +63,7 @@ namespace lib { void pause () { - Lock guard (this); // note recursive lock + Lock guard (this); // note recursive lock for ( uint i=0, lim=(rand() % MAX_PAUSE); i guard (this); + Lock guard (this); step_ = newStep; incrementAll(); } @@ -97,7 +97,7 @@ namespace lib { bool belowLimit () { - Lock guard (this); + Lock guard (this); return cnt_[0] < MAX_SUM; } From 6031ac24659ad4c7c0d64ded0f20a8143e9c5705 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 20 Dec 2008 06:33:36 +0100 Subject: [PATCH 09/25] FIX after rebase --- src/common/subsystemrunner.hpp | 2 +- tests/{common => lib}/concurrency-locking-test.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/{common => lib}/concurrency-locking-test.cpp (98%) diff --git a/src/common/subsystemrunner.hpp b/src/common/subsystemrunner.hpp index 840e35728..ca81d1686 100644 --- a/src/common/subsystemrunner.hpp +++ b/src/common/subsystemrunner.hpp @@ -27,7 +27,7 @@ #include "include/error.hpp" #include "lib/util.hpp" #include "common/subsys.hpp" -#include "lib/multithread.hpp" +#include "lib/concurrency.hpp" #include #include diff --git a/tests/common/concurrency-locking-test.cpp b/tests/lib/concurrency-locking-test.cpp similarity index 98% rename from tests/common/concurrency-locking-test.cpp rename to tests/lib/concurrency-locking-test.cpp index 2bc7065b1..86087e1c5 100644 --- a/tests/common/concurrency-locking-test.cpp +++ b/tests/lib/concurrency-locking-test.cpp @@ -21,11 +21,11 @@ * *****************************************************/ -#include "common/test/run.hpp" +#include "lib/test/run.hpp" //#include "common/util.hpp" -#include "common/error.hpp" +#include "include/error.hpp" -#include "common/concurrency.hpp" +#include "lib/concurrency.hpp" #include From 79b1515f71adb71cef96889b7528530ad1bc8e1e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 21 Dec 2008 20:48:10 +0100 Subject: [PATCH 10/25] typo --- src/proc/assetmanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index fb2623153..24b39dfbc 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -143,11 +143,11 @@ namespace asset throw UnknownID (id); } - /** convienience shortcut for fetching the registered smart-ptr + /** Convenience shortcut for fetching the registered smart-ptr * which is in charge of the given asset instance. By querying * directly asset.id (of type ID), the call to registry.get() * can bypass the dynamic cast, because the type of the asset - * is explicitely given by type KIND. + * is explicitly given by type KIND. */ template P @@ -197,7 +197,7 @@ namespace asset /** * remove the given asset from the internal DB - * together with all its dependants + * together with all its dependents */ void AssetManager::remove (IDA id) From 9240da002fc486bffe1314d9d98f056184417d47 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 22 Dec 2008 04:50:27 +0100 Subject: [PATCH 11/25] WIP --- src/lib/concurrency.hpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lib/concurrency.hpp b/src/lib/concurrency.hpp index fc40a4edf..d03a4edb1 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/concurrency.hpp @@ -40,6 +40,11 @@ #include "include/nobugcfg.h" #include "lib/util.hpp" +extern "C" { +#include "lib/mutex.h" +#include "lib/condition.h" +} + #include #include ///////////////////////////TODO @@ -65,8 +70,16 @@ namespace lib { { struct Monitor { - Monitor() { TODO ("maybe create mutex struct here?"); } - ~Monitor() { TODO ("destroy mutex, assert it isn't locked!"); } /////TODO: Probably need to unlock it silently in case of a class-level monitor? + lumiera_mutex mtx_; + + Monitor() + { + lumiera_recmutex_init (&mtx_, "Monitor object", &NOBUG_FLAG (memory)); + } + ~Monitor() + { + lumiera_mutex_destroy (&mtx_, &NOBUG_FLAG (memory)); + } void acquireLock() { cerr << "acquire Thread Lock\n"; } void releaseLock() { cerr << "release Thread Lock\n"; } @@ -74,7 +87,6 @@ namespace lib { Monitor objectMonitor_; - /////////////////////////////////////////////////////////////////////////TODO: better solution for the storage? Implement the per-this locking without subclassing! /////////////////////////////////////////////////////////////////////////TODO: factor out the recursive/non-recursive mutex case as policy... template @@ -127,7 +139,10 @@ namespace lib { Concurrency::Monitor& Concurrency::getMonitor() { - //TODO: guard this by a Mutex? consider double checked locking? (but then class Monitor needs to be volatile, thus is it worth the effort?) + //TODO: a rather obscure race condition is hidden here: + //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. + //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. + //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" static scoped_ptr classMonitor_ (0); if (!classMonitor_) classMonitor_.reset (new Monitor ()); From 67e95884d5096a51fec4e0a8e8e6406bf445a8a2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 22 Dec 2008 07:23:48 +0100 Subject: [PATCH 12/25] preliminary implementation using pthread primitives. Locking test pass. --- src/include/nobugcfg.h | 2 ++ src/lib/concurrency.hpp | 50 ++++++++++++++++++-------- tests/lib/concurrency-locking-test.cpp | 10 ++---- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/include/nobugcfg.h b/src/include/nobugcfg.h index cfcedd6b7..d8ea8ab26 100644 --- a/src/include/nobugcfg.h +++ b/src/include/nobugcfg.h @@ -88,6 +88,7 @@ namespace lumiera { NOBUG_DECLARE_FLAG (render); ///< logging channel focusing on the render engine's workings NOBUG_DECLARE_FLAG (config); ///< logging channel covering application and session configuration NOBUG_DECLARE_FLAG (memory); ///< logging channel covering memory management issues + NOBUG_DECLARE_FLAG (sync); ///< especially for tracing synchronisation NOBUG_DECLARE_FLAG (test); @@ -109,6 +110,7 @@ namespace lumiera { NOBUG_CPP_DEFINE_FLAG_PARENT (operate, lumiera); NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (render, lumiera, LOG_WARNING); NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (memory, lumiera, LOG_WARNING); + NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (sync, memory, LOG_WARNING); NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (test, all, LOG_ERR); diff --git a/src/lib/concurrency.hpp b/src/lib/concurrency.hpp index d03a4edb1..d54952988 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/concurrency.hpp @@ -47,14 +47,43 @@ extern "C" { #include -#include ///////////////////////////TODO -using std::cerr; namespace lib { using boost::scoped_ptr; + + + namespace sync { // Helpers for Monitor based synchronisation + class RecMutex + { + lumiera_mutex mtx_; + + public: + RecMutex() { lumiera_recmutex_init (&mtx_, "Obj.Monitor RecMutex", &NOBUG_FLAG (sync)); } + ~RecMutex() { lumiera_mutex_destroy (&mtx_, &NOBUG_FLAG (sync)); } + + void acquire() + { + TODO ("Record we may block on mutex"); + + if (pthread_mutex_lock (&mtx_.mutex)) + throw lumiera::error::State("Mutex acquire failed."); ///////TODO capture the error-code + + TODO ("Record we successfully acquired the mutex"); + } + void release() + { + TODO ("Record we are releasing the mutex"); + pthread_mutex_unlock (&mtx_.mutex); + } + }; + + } // namespace sync + + + /** * Facility for monitor object based locking. * To be attached either on a per class base or per object base. @@ -70,19 +99,12 @@ namespace lib { { struct Monitor { - lumiera_mutex mtx_; + sync::RecMutex mtx_; + Monitor() {} + ~Monitor() {} - Monitor() - { - lumiera_recmutex_init (&mtx_, "Monitor object", &NOBUG_FLAG (memory)); - } - ~Monitor() - { - lumiera_mutex_destroy (&mtx_, &NOBUG_FLAG (memory)); - } - - void acquireLock() { cerr << "acquire Thread Lock\n"; } - void releaseLock() { cerr << "release Thread Lock\n"; } + void acquireLock() { mtx_.acquire(); } + void releaseLock() { mtx_.release(); } }; Monitor objectMonitor_; diff --git a/tests/lib/concurrency-locking-test.cpp b/tests/lib/concurrency-locking-test.cpp index 86087e1c5..32f1eb744 100644 --- a/tests/lib/concurrency-locking-test.cpp +++ b/tests/lib/concurrency-locking-test.cpp @@ -22,21 +22,14 @@ #include "lib/test/run.hpp" -//#include "common/util.hpp" #include "include/error.hpp" #include "lib/concurrency.hpp" #include -//#include -//#include #include -//using boost::lexical_cast; -//using boost::format; -//using util::isnil; -//using std::string; using std::cout; using test::Test; @@ -178,7 +171,8 @@ namespace lib { class ConcurrencyLocking_test : public Test { - virtual void run(Arg arg) + virtual void + run (Arg) { if (!Glib::thread_supported()) Glib::thread_init(); From 2b8cd00ab5fa0c05b58b08b1262aed2b9400c77c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 22 Dec 2008 17:00:15 +0100 Subject: [PATCH 13/25] yet another renaming. call it "Sync"... --- src/common/subsystemrunner.hpp | 2 +- src/lib/Makefile.am | 2 +- src/lib/allocationcluster.cpp | 12 ++++++------ src/lib/allocationcluster.hpp | 4 ++-- src/lib/singletonpolicies.hpp | 4 ++-- src/lib/{concurrency.hpp => sync.hpp} | 14 +++++++------- src/lib/visitordispatcher.hpp | 8 ++++---- src/proc/assetmanager.cpp | 6 +++--- src/proc/mobject/session/defsregistry.hpp | 6 +++--- tests/lib/Makefile.am | 1 + ...ency-locking-test.cpp => sync-locking-test.cpp} | 12 ++++++------ 11 files changed, 36 insertions(+), 35 deletions(-) rename src/lib/{concurrency.hpp => sync.hpp} (95%) rename tests/lib/{concurrency-locking-test.cpp => sync-locking-test.cpp} (95%) diff --git a/src/common/subsystemrunner.hpp b/src/common/subsystemrunner.hpp index ca81d1686..3b565de83 100644 --- a/src/common/subsystemrunner.hpp +++ b/src/common/subsystemrunner.hpp @@ -27,7 +27,7 @@ #include "include/error.hpp" #include "lib/util.hpp" #include "common/subsys.hpp" -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include #include diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 6460a0f88..c44284f86 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -68,7 +68,7 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/visitor.hpp \ $(liblumiera_la_srcdir)/visitordispatcher.hpp \ $(liblumiera_la_srcdir)/visitorpolicies.hpp \ - $(liblumiera_la_srcdir)/concurrency.hpp \ + $(liblumiera_la_srcdir)/sync.hpp \ $(liblumiera_la_srcdir)/p.hpp \ $(liblumiera_la_srcdir)/query.hpp \ $(liblumiera_la_srcdir)/singletonfactory.hpp \ diff --git a/src/lib/allocationcluster.cpp b/src/lib/allocationcluster.cpp index 06982df51..b5eb022ef 100644 --- a/src/lib/allocationcluster.cpp +++ b/src/lib/allocationcluster.cpp @@ -81,7 +81,7 @@ namespace lib { void AllocationCluster::MemoryManager::reset (TypeInfo info) { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); if (0 < mem_.size()) purge(); type_ = info; @@ -96,7 +96,7 @@ namespace lib { void AllocationCluster::MemoryManager::purge() { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); REQUIRE (type_.killIt, "we need a deleter function"); REQUIRE (0 < type_.allocSize, "allocation size unknown"); @@ -120,7 +120,7 @@ namespace lib { inline void* AllocationCluster::MemoryManager::allocate() { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); REQUIRE (0 < type_.allocSize); REQUIRE (top_ <= mem_.size()); @@ -140,7 +140,7 @@ namespace lib { inline void AllocationCluster::MemoryManager::commit (void* pendingAlloc) { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); REQUIRE (pendingAlloc); ASSERT (top_ < mem_.size()); @@ -175,7 +175,7 @@ namespace lib { { try { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); TRACE (memory, "shutting down AllocationCluster"); for (size_t i = typeHandlers_.size(); 0 < i; --i) @@ -214,7 +214,7 @@ namespace lib { ASSERT (0 < slot); { - Concurrency::ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? + Sync::ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? if (slot > typeHandlers_.size()) typeHandlers_.resize(slot); diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index dd7ce8959..1e63732cc 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -52,7 +52,7 @@ #include #include "include/error.hpp" -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include "lib/scopedholder.hpp" #include "lib/scopedholdertransfer.hpp" @@ -222,7 +222,7 @@ namespace lib { static TypeInfo setup() { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); if (!id_) id_= ++maxTypeIDs; diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index afbb87253..d0bade0e1 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -34,7 +34,7 @@ This code is heavily inspired by #ifndef LUMIERA_SINGLETONPOLICIES_H #define LUMIERA_SINGLETONPOLICIES_H -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include "include/error.hpp" #include @@ -139,7 +139,7 @@ namespace lumiera struct Multithreaded { typedef volatile S VolatileType; - typedef lib::Concurrency::ClassLock Lock; + typedef lib::Sync::ClassLock Lock; }; diff --git a/src/lib/concurrency.hpp b/src/lib/sync.hpp similarity index 95% rename from src/lib/concurrency.hpp rename to src/lib/sync.hpp index d54952988..123bb06c7 100644 --- a/src/lib/concurrency.hpp +++ b/src/lib/sync.hpp @@ -21,7 +21,7 @@ */ -/** @file concurrency.hpp +/** @file sync.hpp ** Collection of helpers and wrappers to support dealing with concurrency issues. ** Actually, everything is implemented either by the Lumiera backend, or directly ** by pthread. The purpose is to support and automate the most common use cases @@ -95,7 +95,7 @@ namespace lib { * * @todo actually implement this facility using the Lumiera backend. */ - struct Concurrency + struct Sync { struct Monitor { @@ -114,7 +114,7 @@ namespace lib { template static inline Monitor& getMonitor(); - static inline Monitor& getMonitor(Concurrency* forThis); + static inline Monitor& getMonitor(Sync* forThis); class Lock @@ -150,16 +150,16 @@ namespace lib { - Concurrency::Monitor& - Concurrency::getMonitor(Concurrency* forThis) + Sync::Monitor& + Sync::getMonitor(Sync* forThis) { REQUIRE (forThis); return forThis->objectMonitor_; } template - Concurrency::Monitor& - Concurrency::getMonitor() + Sync::Monitor& + Sync::getMonitor() { //TODO: a rather obscure race condition is hidden here: //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index de56445b9..7977d7003 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -26,7 +26,7 @@ #define LUMIERA_VISITORDISPATCHER_H #include "include/error.hpp" -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include "lib/singleton.hpp" #include "lib/util.hpp" @@ -36,7 +36,7 @@ namespace lumiera { namespace visitor { - using lib::Concurrency; + using lib::Sync; template class Tag; @@ -62,7 +62,7 @@ namespace lumiera { static void generateID (size_t& id) { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); if (!id) id = ++lastRegisteredID; } @@ -138,7 +138,7 @@ namespace lumiera { void accomodate (size_t index) { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); if (index > table_.size()) table_.resize (index); // performance bottleneck?? TODO: measure the real impact! } diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 24b39dfbc..960699189 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -24,7 +24,7 @@ #include "proc/assetmanager.hpp" #include "proc/asset/db.hpp" -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include "lib/util.hpp" #include @@ -39,7 +39,7 @@ using boost::bind; using util::for_each; using lumiera::Singleton; -using lib::Concurrency; +using lib::Sync; namespace asset @@ -116,7 +116,7 @@ namespace asset TODO ("check validity of Ident Category"); ID asset_id (getID (idi)); - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 8a3fcdfd5..ad4721b04 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -40,7 +40,7 @@ #define MOBJECT_SESSION_DEFSREGISTRY_H -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include "lib/query.hpp" #include "lib/util.hpp" #include "lib/p.hpp" @@ -58,7 +58,7 @@ namespace mobject { using lumiera::P; using lumiera::Query; - using lib::Concurrency; + using lib::Sync; using std::tr1::weak_ptr; using std::string; @@ -162,7 +162,7 @@ namespace mobject { static void createSlot (Table& table) { - Concurrency::ClassLock guard(); + Sync::ClassLock guard(); if (!index) index = ++maxSlots; if (index > table.size()) diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index b1366bf3f..19a13ce2f 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -58,6 +58,7 @@ test_lib_SOURCES = \ $(testlib_srcdir)/singletontestmocktest.cpp \ $(testlib_srcdir)/streamtypebasicstest.cpp \ $(testlib_srcdir)/streamtypelifecycletest.cpp \ + $(testlib_srcdir)/sync-locking-test.cpp \ $(testlib_srcdir)/test/cmdlinewrappertest.cpp \ $(testlib_srcdir)/test/testoptiontest.cpp \ $(testlib_srcdir)/vectortransfertest.cpp \ diff --git a/tests/lib/concurrency-locking-test.cpp b/tests/lib/sync-locking-test.cpp similarity index 95% rename from tests/lib/concurrency-locking-test.cpp rename to tests/lib/sync-locking-test.cpp index 32f1eb744..884b030e7 100644 --- a/tests/lib/concurrency-locking-test.cpp +++ b/tests/lib/sync-locking-test.cpp @@ -1,5 +1,5 @@ /* - ConcurrencyLocking(Test) - check the monitor object based locking + SyncLocking(Test) - check the monitor object based locking Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -24,7 +24,7 @@ #include "lib/test/run.hpp" #include "include/error.hpp" -#include "lib/concurrency.hpp" +#include "lib/sync.hpp" #include @@ -48,7 +48,7 @@ namespace lib { class Victim - : public Concurrency + : public Sync { volatile long cnt_[NUM_COUNTERS]; volatile uint step_; ///< @note stored as instance variable @@ -166,9 +166,9 @@ namespace lib { * But because the class Victim uses an object level monitor to * guard the mutations, the state should remain consistent. * - * @see concurrency.hpp + * @see sync.hpp */ - class ConcurrencyLocking_test : public Test + class SyncLocking_test : public Test { virtual void @@ -198,7 +198,7 @@ namespace lib { /** Register this test class... */ - LAUNCHER (ConcurrencyLocking_test, "unit common"); + LAUNCHER (SyncLocking_test, "unit common"); From 43521e394547db4ca851e814da4c5f3924d9a2e3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 23 Dec 2008 00:03:04 +0100 Subject: [PATCH 14/25] building block for the Condition part of the Monitor pattern --- src/lib/sync.hpp | 65 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 123bb06c7..27b72f5c3 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -39,6 +39,7 @@ #include "include/nobugcfg.h" #include "lib/util.hpp" +#include "include/error.hpp" extern "C" { #include "lib/mutex.h" @@ -46,7 +47,8 @@ extern "C" { } #include - +#include +#include namespace lib { @@ -54,30 +56,75 @@ namespace lib { using boost::scoped_ptr; - namespace sync { // Helpers for Monitor based synchronisation + /** Helpers and building blocks for Monitor based synchronisation */ + namespace sync { + class RecMutex { lumiera_mutex mtx_; - public: - RecMutex() { lumiera_recmutex_init (&mtx_, "Obj.Monitor RecMutex", &NOBUG_FLAG (sync)); } - ~RecMutex() { lumiera_mutex_destroy (&mtx_, &NOBUG_FLAG (sync)); } + pthread_mutex_t* get () { return &mtx_.mutex; } + friend class Condition; - void acquire() + public: + RecMutex() { lumiera_recmutex_init (&mtx_, "Obj.Monitor RecMutex", &NOBUG_FLAG(sync)); } + ~RecMutex() { lumiera_mutex_destroy (&mtx_, &NOBUG_FLAG(sync)); } + + void + acquire() { TODO ("Record we may block on mutex"); - if (pthread_mutex_lock (&mtx_.mutex)) + if (pthread_mutex_lock (get())) throw lumiera::error::State("Mutex acquire failed."); ///////TODO capture the error-code TODO ("Record we successfully acquired the mutex"); } - void release() + + void + release() { TODO ("Record we are releasing the mutex"); - pthread_mutex_unlock (&mtx_.mutex); + pthread_mutex_unlock (get()); } + + }; + + + class Condition + { + lumiera_condition cond_; + + public: + Condition() { lumiera_condition_init (&cond_, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } + ~Condition() { lumiera_condition_destroy (&cond_, &NOBUG_FLAG(sync) ); } + + void + signal (bool wakeAll=false) + { + if (wakeAll) + pthread_cond_broadcast (&cond_.cond); + else + pthread_cond_signal (&cond_.cond); + } + + template + bool + wait (volatile BF const& predicate, RecMutex& mtx, timespec* waitEndTime=0) + { + int err=0; + while (!predicate() && !err) + if (waitEndTime) + err = pthread_cond_timedwait (&cond_.cond, mtx.get(), waitEndTime); + else + err = pthread_cond_wait (&cond_.cond, mtx.get()); + + if (!err) return true; + if (ETIMEDOUT==err) return false; + + throw lumiera::error::State ("Condition wait failed."); ///////////TODO extract error-code + } }; } // namespace sync From 59a7270f5dd06017021eb08bf61bfe7b8214ea9b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 23 Dec 2008 01:32:01 +0100 Subject: [PATCH 15/25] draft test for wait/notify, fails without the implementation --- src/lib/sync.hpp | 2 + tests/lib/sync-waiting-test.cpp | 163 ++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 tests/lib/sync-waiting-test.cpp diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 27b72f5c3..d614c96f1 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -147,6 +147,8 @@ namespace lib { struct Monitor { sync::RecMutex mtx_; + sync::Condition cond_; + Monitor() {} ~Monitor() {} diff --git a/tests/lib/sync-waiting-test.cpp b/tests/lib/sync-waiting-test.cpp new file mode 100644 index 000000000..5a0a69a04 --- /dev/null +++ b/tests/lib/sync-waiting-test.cpp @@ -0,0 +1,163 @@ +/* + SyncWaiting(Test) - check the monitor object based wait/notification + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "include/error.hpp" + +#include "lib/sync.hpp" + +#include + +#include + +using std::cout; +using test::Test; + + +namespace lib { + 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) {} + }; + + + class SyncOnBool + : public Token + { + 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(); + } + }; + + } // (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 an additionally uses an embedded condition. We provide + * several pre-configured ways of specifying the condition to wait upon. + * + * @see sync.hpp + */ + class SyncWaiting_test : public Test + { + + virtual void + run (Arg) + { + if (!Glib::thread_supported()) + Glib::thread_init(); + + SyncOnBool use_sync_var; + waitPingPong (use_sync_var); + } + + + /** + * Helper actually performing the test: + * creates two threads and let them block and wait cross-wise. + * @param tok object containing the monitor and condition to be tested. + */ + void + waitPingPong (Token& tok) + { + 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()); + + sleep (1); // 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 test + +} // namespace lumiera From 36704a856ee5665b4cdecd953dd9033b36692946 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 23 Dec 2008 04:27:11 +0100 Subject: [PATCH 16/25] basic wait/notify impl added, waiting-test pass --- src/common/subsys.cpp | 2 +- src/common/subsystemrunner.hpp | 11 ++--- src/lib/sync.hpp | 76 ++++++++++++++++++++++----------- tests/lib/sync-waiting-test.cpp | 9 ++-- 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/common/subsys.cpp b/src/common/subsys.cpp index 41f2fb845..44fc9075d 100644 --- a/src/common/subsys.cpp +++ b/src/common/subsys.cpp @@ -53,7 +53,7 @@ namespace lumiera { bool Subsys::isRunning() { - //Lock guard (*this); + //Lock guard (this); return checkRunningState(); } diff --git a/src/common/subsystemrunner.hpp b/src/common/subsystemrunner.hpp index 3b565de83..da1d39360 100644 --- a/src/common/subsystemrunner.hpp +++ b/src/common/subsystemrunner.hpp @@ -87,6 +87,7 @@ namespace lumiera { * @see main.cpp */ class SubsystemRunner +// : Sync { Option& opts_; volatile bool emergency_; @@ -108,7 +109,7 @@ namespace lumiera { void maybeRun (Subsys& susy) { - //Lock guard (*this); + //Lock guard (this); if (!susy.isRunning() && susy.shouldStart (opts_)) triggerStartup (&susy); @@ -119,14 +120,14 @@ namespace lumiera { void shutdownAll () { - //Lock guard (*this); + //Lock guard (this); for_each (running_, killIt_); } bool wait () { - //Lock(*this).wait (&SubsystemRunner::allDead); + //Lock(this).wait (&SubsystemRunner::allDead); return isEmergencyExit(); } @@ -160,7 +161,7 @@ namespace lumiera { sigTerm (Subsys* susy, Error* problem) ///< called from subsystem on termination { ASSERT (susy); - //Lock guard (*this); + //Lock guard (this); triggerEmergency(problem); ERROR_IF (susy->isRunning(), lumiera, "Subsystem '%s' signals termination, " "without resetting running state", cStr(*susy)); @@ -173,7 +174,7 @@ namespace lumiera { allDead () { if (isEmergencyExit()) - ; //Lock(*this).setTimeout(EMERGENCYTIMEOUT); + ; //Lock(this).setTimeout(EMERGENCYTIMEOUT); return isnil (running_); // end wait if no running subsystem left } diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index d614c96f1..47dc591ec 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -97,7 +97,7 @@ namespace lib { lumiera_condition cond_; public: - Condition() { lumiera_condition_init (&cond_, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } + Condition() { lumiera_condition_init (&cond_, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } ~Condition() { lumiera_condition_destroy (&cond_, &NOBUG_FLAG(sync) ); } void @@ -109,9 +109,10 @@ namespace lib { pthread_cond_signal (&cond_.cond); } + template bool - wait (volatile BF const& predicate, RecMutex& mtx, timespec* waitEndTime=0) + wait (BF& predicate, RecMutex& mtx, timespec* waitEndTime=0) { int err=0; while (!predicate() && !err) @@ -127,6 +128,43 @@ namespace lib { } }; + + struct Monitor + { + sync::RecMutex mtx_; + sync::Condition cond_; + + Monitor() {} + ~Monitor() {} + + void acquireLock() { mtx_.acquire(); } + void releaseLock() { mtx_.release(); } + + void signal(bool a){ cond_.signal(a);} + + inline bool wait (volatile bool&, ulong); + }; + + + typedef volatile bool& Flag; + + struct BoolFlagPredicate + { + Flag flag_; + BoolFlagPredicate (Flag f) : flag_(f) {} + + bool operator() () { return flag_; } + }; + + + bool + Monitor::wait(Flag flag, ulong timeoout) + { + BoolFlagPredicate checkFlag(flag); + return cond_.wait(checkFlag, mtx_, (timespec*)0); + } + + } // namespace sync @@ -144,17 +182,7 @@ namespace lib { */ struct Sync { - struct Monitor - { - sync::RecMutex mtx_; - sync::Condition cond_; - - Monitor() {} - ~Monitor() {} - - void acquireLock() { mtx_.acquire(); } - void releaseLock() { mtx_.release(); } - }; + typedef sync::Monitor Monitor; Monitor objectMonitor_; @@ -169,24 +197,22 @@ namespace lib { class Lock { Monitor& mon_; + public: template - Lock(X* it) : mon_(getMonitor(it)) - { - mon_.acquireLock(); - } + Lock(X* it) : mon_(getMonitor(it)){ mon_.acquireLock(); } + Lock(Monitor& m) : mon_(m) { mon_.acquireLock(); } + ~Lock() { mon_.releaseLock(); } - Lock(Monitor& m) : mon_(m) - { - mon_.acquireLock(); - } + template + bool wait (C& cond, ulong time=0) { return mon_.wait(cond,time);} + + void notifyAll() { mon_.signal(true); } + void notify() { mon_.signal(false);} - ~Lock() - { - mon_.releaseLock(); - } }; + template struct ClassLock : Lock { diff --git a/tests/lib/sync-waiting-test.cpp b/tests/lib/sync-waiting-test.cpp index 5a0a69a04..3bc087258 100644 --- a/tests/lib/sync-waiting-test.cpp +++ b/tests/lib/sync-waiting-test.cpp @@ -65,7 +65,8 @@ namespace lib { class SyncOnBool - : public Token + : public Token, + public Sync { bool got_new_data_; @@ -74,16 +75,16 @@ namespace lib { void getIt() { - //Lock(*this).wait (got_new_data_); + Lock(this).wait (got_new_data_); sum_ += input_; } void provide (uint val) { - //Lock sync(*this); + Lock sync(this); input_ = val; got_new_data_ = true; - //sync.notifyAll(); + sync.notifyAll(); } }; From ee9cd8b63231d77fe4fc7f170faf30496e33684d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 24 Dec 2008 03:30:34 +0100 Subject: [PATCH 17/25] typos --- src/include/error.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/include/error.hpp b/src/include/error.hpp index bce21423c..50d43187d 100644 --- a/src/include/error.hpp +++ b/src/include/error.hpp @@ -40,7 +40,7 @@ namespace lumiera { LUMIERA_ERROR_DECLARE(EXCEPTION); /** - * Interface and Baseclass of all Exceptions thrown + * Interface and Base class of all Exceptions thrown * from within Lumiera (C++) code. Common operations * for getting an diagnostic message and for obtaining * the root cause, i.e. the first exception encountered @@ -56,7 +56,7 @@ namespace lumiera { Error (const Error&) throw(); virtual ~Error () throw() {}; - /** yield a diagnostic message characterizing the problem */ + /** yield a diagnostic message characterising the problem */ virtual const char* what () const throw(); /** the internal Lumiera-error-ID (was set as C-errorstate in ctor) */ @@ -68,12 +68,12 @@ namespace lumiera { /** If this exception was caused by a chain of further exceptions, * return the description of the first one registered in this throw sequence. * This works only if every exceptions thrown as a consequence of another exception - * is propperly constructed by passing the original exception to the constructor + * is properly constructed by passing the original exception to the constructor * @return the description string, maybe empty (if there is no known root cause) */ const string& rootCause () const throw() { return this->cause_; } - /** replace the previous or default friendly message for the user. To be localized. */ + /** replace the previous or default friendly message for the user. To be localised. */ Error& setUsermsg (const string& newMsg) throw() { this->msg_ = newMsg; return *this; } /** give additional developer info. Typically used at intermediate handlers to add context. */ @@ -82,10 +82,10 @@ namespace lumiera { private: const char* id_; ///< an LUMIERA_ERROR id, which is set as errorstate on construction - string msg_; ///< friendly message intended for users (to be localized) + string msg_; ///< friendly message intended for users (to be localised) string desc_; ///< detailed description of the error situation for the developers mutable string what_; ///< buffer for generating the detailed description on demand - const string cause_; ///< descriptoin of first exception encountered in the chain + const string cause_; ///< description of first exception encountered in the chain static const string extractCauseMsg (const std::exception&) throw(); }; @@ -95,7 +95,7 @@ namespace lumiera { - /* === Exception Subcategories === */ + /* === Exception Sub-categories === */ namespace error { @@ -123,10 +123,10 @@ namespace lumiera { /** Macro for creating derived exception classes properly * integrated into Lumiera's exception hierarchy. Using - * this macro asures that the new class will get the full + * this macro assures that the new class will get the full * set of constructors and behaviour common to all exception * classes, so it should be used when creating an derived - * exception type for more then stricly local purposes + * exception type for more then strictly local purposes */ #define LUMIERA_EXCEPTION_DECLARE(CLASS, PARENT, _ID_) \ class CLASS : public PARENT \ From 2650216d9b8ab71816cbee73664a70cbca7b7444 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 24 Dec 2008 03:31:35 +0100 Subject: [PATCH 18/25] Refactoring I: better put the timeout explicitly separate --- src/lib/sync.hpp | 80 +++++++++++++++++++++++++------- tests/lib/meta/generatortest.cpp | 2 +- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 47dc591ec..7968b0df7 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -28,14 +28,15 @@ ** in object oriented style. ** ** @see mutex.h - ** @see concurrency-locking-test.cpp + ** @see sync-locking-test.cpp + ** @see sync-waiting-test.cpp ** @see asset::AssetManager::reg() usage example ** @see subsystemrunner.hpp usage example */ -#ifndef LIB_CONCURRENCY_H -#define LIB_CONCURRENCY_H +#ifndef LIB_SYNC_H +#define LIB_SYNC_H #include "include/nobugcfg.h" #include "lib/util.hpp" @@ -59,7 +60,8 @@ namespace lib { /** Helpers and building blocks for Monitor based synchronisation */ namespace sync { - + class Timeout; + class RecMutex { lumiera_mutex mtx_; @@ -110,14 +112,14 @@ namespace lib { } - template + template bool - wait (BF& predicate, RecMutex& mtx, timespec* waitEndTime=0) + wait (BF& predicate, MTX& mtx, Timeout& waitEndTime) { int err=0; while (!predicate() && !err) if (waitEndTime) - err = pthread_cond_timedwait (&cond_.cond, mtx.get(), waitEndTime); + err = pthread_cond_timedwait (&cond_.cond, mtx.get(), &waitEndTime); else err = pthread_cond_wait (&cond_.cond, mtx.get()); @@ -128,12 +130,49 @@ namespace lib { } }; - - struct Monitor + /** helper for specifying an optional timeout + * for an timed wait. It wraps a timespec-struct + * and allows for easy initialisation by a given + * relative offset. + */ + struct Timeout + : timespec { - sync::RecMutex mtx_; - sync::Condition cond_; + Timeout() { tv_sec=tv_nsec=0; } + /** initialise to NOW() + offset (in milliseconds) */ + Timeout& + setOffset (ulong offs) + { + if (offs) + { + clock_gettime(CLOCK_REALTIME, this); + tv_sec += offs / 1000; + tv_nsec += 1000000 * (offs % 1000); + if (tv_nsec > 1000000000) + { + tv_sec += tv_nsec / 1000000000; + tv_nsec %= 1000000000; + } } + return *this; + } + + operator bool() { return 0 != tv_sec; } // allows if (timeout_).... + }; + + + typedef volatile bool& Flag; + + class Monitor + { + RecMutex mtx_; + Condition cond_; + + Timeout timeout_; + + //////TODO my intention is to make two variants of the monitor, where the simple one leaves out the condition part + + public: Monitor() {} ~Monitor() {} @@ -142,11 +181,12 @@ namespace lib { void signal(bool a){ cond_.signal(a);} - inline bool wait (volatile bool&, ulong); + inline bool wait (Flag, ulong); + inline void setTimeout(ulong); + inline bool isTimedWait(); }; - typedef volatile bool& Flag; struct BoolFlagPredicate { @@ -158,12 +198,18 @@ namespace lib { bool - Monitor::wait(Flag flag, ulong timeoout) + Monitor::wait (Flag flag, ulong timedwait) { BoolFlagPredicate checkFlag(flag); - return cond_.wait(checkFlag, mtx_, (timespec*)0); + return cond_.wait(checkFlag, mtx_, timeout_.setOffset(timedwait)); } - + + void + Monitor::setTimeout (ulong relative) {timeout_.setOffset(relative);} + + bool + Monitor::isTimedWait () {return (timeout_);} + } // namespace sync @@ -206,6 +252,7 @@ namespace lib { template bool wait (C& cond, ulong time=0) { return mon_.wait(cond,time);} + void setTimeout(ulong time) { mon_.setTimeout(time); } void notifyAll() { mon_.signal(true); } void notify() { mon_.signal(false);} @@ -240,6 +287,7 @@ namespace lib { //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" + //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode static scoped_ptr classMonitor_ (0); if (!classMonitor_) classMonitor_.reset (new Monitor ()); diff --git a/tests/lib/meta/generatortest.cpp b/tests/lib/meta/generatortest.cpp index 932b79fe0..c4224b4fa 100644 --- a/tests/lib/meta/generatortest.cpp +++ b/tests/lib/meta/generatortest.cpp @@ -120,7 +120,7 @@ namespace lumiera */ class TypeListGenerator_test : public Test { - virtual void run(Arg arg) + virtual void run(Arg) { NumberBabbler me_can_has_more_numberz; From be2daf64ff7c815c80d37152d8c940f4fa202d93 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 24 Dec 2008 03:42:36 +0100 Subject: [PATCH 19/25] whitespace (trying to compare LUMIERA_MUTEX_SECTION and LUMIERA_RECMUTEX_SECTION) --- src/lib/mutex.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/lib/mutex.h b/src/lib/mutex.h index 86fb48944..0111c9fff 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -101,26 +101,26 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); /** * Recursive Mutual exclusive section. */ -#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ - NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ +#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ \ + = {(LumieraMutex)1 NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ })) From 1ff7f0c656d389fc5705e263fdcb7caad31763c0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 24 Dec 2008 23:23:23 +0100 Subject: [PATCH 20/25] Refactoring II: encapsulate the variants probvieded by the backend as base classes --- src/lib/sync.hpp | 116 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 7968b0df7..4e295909a 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -60,55 +60,105 @@ namespace lib { /** Helpers and building blocks for Monitor based synchronisation */ namespace sync { - class Timeout; - - class RecMutex + + struct Wrapped_LumieraExcMutex + : public lumiera_mutex + { + protected: + Wrapped_LumieraExcMutex() { lumiera_mutex_init (this, "Obj.Monitor ExclMutex", &NOBUG_FLAG(sync)); } + ~Wrapped_LumieraExcMutex() { lumiera_mutex_destroy (this, &NOBUG_FLAG(sync)); } + + //------------------Resource-Tracking------ + void __may_block() { TODO ("Record we may block on mutex"); } + void __enter() { TODO ("Record we successfully acquired the mutex"); } + void __leave() { TODO ("Record we are releasing the mutex"); } + }; + + + struct Wrapped_LumieraRecMutex + : public lumiera_mutex + { + protected: + Wrapped_LumieraRecMutex() { lumiera_recmutex_init (this, "Obj.Monitor ExclMutex", &NOBUG_FLAG(sync)); } + ~Wrapped_LumieraRecMutex() { lumiera_mutex_destroy (this, &NOBUG_FLAG(sync)); } + + //------------------Resource-Tracking------ + void __may_block() { TODO ("Record we may block on mutex"); } + void __enter() { TODO ("Record we successfully acquired the mutex"); } + void __leave() { TODO ("Record we are releasing the mutex"); } + }; + + + struct Wrapped_LumieraExcCond + : public lumiera_condition + { + protected: + Wrapped_LumieraExcCond() { lumiera_condition_init (this, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } + ~Wrapped_LumieraExcCond() { lumiera_condition_destroy (this, &NOBUG_FLAG(sync) ); } + + //------------------Resource-Tracking------ + void __may_block() { TODO ("Record we may block on mutex"); } + void __enter() { TODO ("Record we successfully acquired the mutex"); } + void __leave() { TODO ("Record we are releasing the mutex"); } + }; + + + struct Wrapped_LumieraRecCond + : public lumiera_condition + { + protected: + Wrapped_LumieraRecCond() { lumiera_condition_init (this, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } ////////TODO + ~Wrapped_LumieraRecCond() { lumiera_condition_destroy (this, &NOBUG_FLAG(sync) ); } + + //------------------Resource-Tracking------ + void __may_block() { TODO ("Record we may block on mutex"); } + void __enter() { TODO ("Record we successfully acquired the mutex"); } + void __leave() { TODO ("Record we are releasing the mutex"); } + }; + + + template + class Mutex + : protected MTX { - lumiera_mutex mtx_; - - pthread_mutex_t* get () { return &mtx_.mutex; } - friend class Condition; - public: - RecMutex() { lumiera_recmutex_init (&mtx_, "Obj.Monitor RecMutex", &NOBUG_FLAG(sync)); } - ~RecMutex() { lumiera_mutex_destroy (&mtx_, &NOBUG_FLAG(sync)); } - void acquire() { - TODO ("Record we may block on mutex"); + __may_block(); - if (pthread_mutex_lock (get())) - throw lumiera::error::State("Mutex acquire failed."); ///////TODO capture the error-code - - TODO ("Record we successfully acquired the mutex"); + if (pthread_mutex_lock (&mutex)) + throw lumiera::error::State("Mutex acquire failed."); ///////TODO capture the error-code + + __enter(); } void release() { - TODO ("Record we are releasing the mutex"); - pthread_mutex_unlock (get()); + __leave(); + + pthread_mutex_unlock (&mutex); } }; + class Timeout; + + + template class Condition + : public Mutex { - lumiera_condition cond_; - public: - Condition() { lumiera_condition_init (&cond_, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } - ~Condition() { lumiera_condition_destroy (&cond_, &NOBUG_FLAG(sync) ); } - void signal (bool wakeAll=false) { if (wakeAll) - pthread_cond_broadcast (&cond_.cond); + pthread_cond_broadcast (&cond); else - pthread_cond_signal (&cond_.cond); + pthread_cond_signal (&cond); } @@ -119,9 +169,9 @@ namespace lib { int err=0; while (!predicate() && !err) if (waitEndTime) - err = pthread_cond_timedwait (&cond_.cond, mtx.get(), &waitEndTime); + err = pthread_cond_timedwait (&cond, &mutex, &waitEndTime); else - err = pthread_cond_wait (&cond_.cond, mtx.get()); + err = pthread_cond_wait (&cond, &mutex); if (!err) return true; if (ETIMEDOUT==err) return false; @@ -164,10 +214,8 @@ namespace lib { typedef volatile bool& Flag; class Monitor + : Condition /////////TODO: to be refactored. This is just one example, using the non-recursive condition { - RecMutex mtx_; - Condition cond_; - Timeout timeout_; //////TODO my intention is to make two variants of the monitor, where the simple one leaves out the condition part @@ -176,10 +224,10 @@ namespace lib { Monitor() {} ~Monitor() {} - void acquireLock() { mtx_.acquire(); } - void releaseLock() { mtx_.release(); } + void acquireLock() { acquire(); } + void releaseLock() { release(); } - void signal(bool a){ cond_.signal(a);} + void signal(bool a){ signal(a); } inline bool wait (Flag, ulong); inline void setTimeout(ulong); @@ -211,7 +259,7 @@ namespace lib { Monitor::isTimedWait () {return (timeout_);} - } // namespace sync + } // namespace sync (helpers and building blocks) From 54e88e691499eb71fc60cb2ac2dd9a131c6aa5ad Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 26 Dec 2008 01:50:32 +0100 Subject: [PATCH 21/25] Refactoring III: Recursive/Nonrecursive and Waitable as policy classes pass test again --- src/lib/allocationcluster.cpp | 12 +- src/lib/allocationcluster.hpp | 2 +- src/lib/singletonpolicies.hpp | 2 +- src/lib/sync.hpp | 137 +++++++++++----------- src/lib/visitordispatcher.hpp | 4 +- src/proc/assetmanager.cpp | 2 +- src/proc/mobject/session/defsregistry.hpp | 2 +- tests/lib/sync-locking-test.cpp | 2 +- tests/lib/sync-waiting-test.cpp | 2 +- 9 files changed, 82 insertions(+), 83 deletions(-) diff --git a/src/lib/allocationcluster.cpp b/src/lib/allocationcluster.cpp index b5eb022ef..4dafb7d48 100644 --- a/src/lib/allocationcluster.cpp +++ b/src/lib/allocationcluster.cpp @@ -81,7 +81,7 @@ namespace lib { void AllocationCluster::MemoryManager::reset (TypeInfo info) { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); if (0 < mem_.size()) purge(); type_ = info; @@ -96,7 +96,7 @@ namespace lib { void AllocationCluster::MemoryManager::purge() { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); REQUIRE (type_.killIt, "we need a deleter function"); REQUIRE (0 < type_.allocSize, "allocation size unknown"); @@ -120,7 +120,7 @@ namespace lib { inline void* AllocationCluster::MemoryManager::allocate() { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); REQUIRE (0 < type_.allocSize); REQUIRE (top_ <= mem_.size()); @@ -140,7 +140,7 @@ namespace lib { inline void AllocationCluster::MemoryManager::commit (void* pendingAlloc) { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); REQUIRE (pendingAlloc); ASSERT (top_ < mem_.size()); @@ -175,7 +175,7 @@ namespace lib { { try { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); TRACE (memory, "shutting down AllocationCluster"); for (size_t i = typeHandlers_.size(); 0 < i; --i) @@ -214,7 +214,7 @@ namespace lib { ASSERT (0 < slot); { - Sync::ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? + Sync<>::ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? if (slot > typeHandlers_.size()) typeHandlers_.resize(slot); diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index 1e63732cc..51200e740 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -222,7 +222,7 @@ namespace lib { static TypeInfo setup() { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); if (!id_) id_= ++maxTypeIDs; diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index d0bade0e1..83c76f5ca 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -139,7 +139,7 @@ namespace lumiera struct Multithreaded { typedef volatile S VolatileType; - typedef lib::Sync::ClassLock Lock; + typedef lib::Sync<>::ClassLock Lock; }; diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 4e295909a..b0a9df32a 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -121,6 +121,12 @@ namespace lib { class Mutex : protected MTX { + protected: + using MTX::mutex; + using MTX::__may_block; + using MTX::__enter; + using MTX::__leave; + public: void acquire() @@ -151,6 +157,10 @@ namespace lib { class Condition : public Mutex { + protected: + using CDX::cond; + using CDX::mutex; + public: void signal (bool wakeAll=false) @@ -162,9 +172,9 @@ namespace lib { } - template + template bool - wait (BF& predicate, MTX& mtx, Timeout& waitEndTime) + wait (BF& predicate, Timeout& waitEndTime) { int err=0; while (!predicate() && !err) @@ -210,32 +220,8 @@ namespace lib { operator bool() { return 0 != tv_sec; } // allows if (timeout_).... }; - typedef volatile bool& Flag; - class Monitor - : Condition /////////TODO: to be refactored. This is just one example, using the non-recursive condition - { - Timeout timeout_; - - //////TODO my intention is to make two variants of the monitor, where the simple one leaves out the condition part - - public: - Monitor() {} - ~Monitor() {} - - void acquireLock() { acquire(); } - void releaseLock() { release(); } - - void signal(bool a){ signal(a); } - - inline bool wait (Flag, ulong); - inline void setTimeout(ulong); - inline bool isTimedWait(); - }; - - - struct BoolFlagPredicate { Flag flag_; @@ -244,25 +230,48 @@ namespace lib { bool operator() () { return flag_; } }; + + template + class Monitor + : IMPL + { + Timeout timeout_; + + public: + Monitor() {} + ~Monitor() {} + + void acquireLock() { IMPL::acquire(); } + void releaseLock() { IMPL::release(); } + + void signal(bool a){ IMPL::signal(a); } + + bool wait (Flag flag, ulong timedwait=0) + { + BoolFlagPredicate checkFlag(flag); + return IMPL::wait(checkFlag, timeout_.setOffset(timedwait)); + } + + void setTimeout(ulong relative) {timeout_.setOffset(relative);} + bool isTimedWait() {return (timeout_);} + }; - bool - Monitor::wait (Flag flag, ulong timedwait) - { - BoolFlagPredicate checkFlag(flag); - return cond_.wait(checkFlag, mtx_, timeout_.setOffset(timedwait)); - } - - void - Monitor::setTimeout (ulong relative) {timeout_.setOffset(relative);} - - bool - Monitor::isTimedWait () {return (timeout_);} + typedef Mutex NonrecursiveLock_NoWait; + typedef Mutex RecursiveLock_NoWait; + typedef Condition NonrecursiveLock_Waitable; + typedef Condition RecursiveLock_Waitable; } // namespace sync (helpers and building blocks) + using sync::NonrecursiveLock_NoWait; + using sync::NonrecursiveLock_Waitable; + using sync::RecursiveLock_NoWait; + using sync::RecursiveLock_Waitable; + + /** * Facility for monitor object based locking. * To be attached either on a per class base or per object base. @@ -274,18 +283,33 @@ namespace lib { * * @todo actually implement this facility using the Lumiera backend. */ + template struct Sync { - typedef sync::Monitor Monitor; - + typedef sync::Monitor Monitor; Monitor objectMonitor_; - /////////////////////////////////////////////////////////////////////////TODO: factor out the recursive/non-recursive mutex case as policy... + static Monitor& + getMonitor(Sync* forThis) + { + REQUIRE (forThis); + return forThis->objectMonitor_; + } template - static inline Monitor& getMonitor(); - - static inline Monitor& getMonitor(Sync* forThis); + static Monitor& + getMonitor() + { + //TODO: a rather obscure race condition is hidden here: + //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. + //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. + //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" + //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode + + static scoped_ptr classMonitor_ (0); + if (!classMonitor_) classMonitor_.reset (new Monitor ()); + return *classMonitor_; + } class Lock @@ -316,31 +340,6 @@ namespace lib { }; - - - - - Sync::Monitor& - Sync::getMonitor(Sync* forThis) - { - REQUIRE (forThis); - return forThis->objectMonitor_; - } - - template - Sync::Monitor& - Sync::getMonitor() - { - //TODO: a rather obscure race condition is hidden here: - //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. - //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. - //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" - //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode - - static scoped_ptr classMonitor_ (0); - if (!classMonitor_) classMonitor_.reset (new Monitor ()); - return *classMonitor_; - } } // namespace lumiera diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 7977d7003..4c9145716 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -62,7 +62,7 @@ namespace lumiera { static void generateID (size_t& id) { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); if (!id) id = ++lastRegisteredID; } @@ -138,7 +138,7 @@ namespace lumiera { void accomodate (size_t index) { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); if (index > table_.size()) table_.resize (index); // performance bottleneck?? TODO: measure the real impact! } diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 960699189..939162103 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -116,7 +116,7 @@ namespace asset TODO ("check validity of Ident Category"); ID asset_id (getID (idi)); - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index ad4721b04..f377c3fe6 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -162,7 +162,7 @@ namespace mobject { static void createSlot (Table& table) { - Sync::ClassLock guard(); + Sync<>::ClassLock guard(); if (!index) index = ++maxSlots; if (index > table.size()) diff --git a/tests/lib/sync-locking-test.cpp b/tests/lib/sync-locking-test.cpp index 884b030e7..e246a8083 100644 --- a/tests/lib/sync-locking-test.cpp +++ b/tests/lib/sync-locking-test.cpp @@ -48,7 +48,7 @@ namespace lib { class Victim - : public Sync + : public Sync { volatile long cnt_[NUM_COUNTERS]; volatile uint step_; ///< @note stored as instance variable diff --git a/tests/lib/sync-waiting-test.cpp b/tests/lib/sync-waiting-test.cpp index 3bc087258..1cc8a9398 100644 --- a/tests/lib/sync-waiting-test.cpp +++ b/tests/lib/sync-waiting-test.cpp @@ -66,7 +66,7 @@ namespace lib { class SyncOnBool : public Token, - public Sync + public Sync { bool got_new_data_; From eaedab90ea5635dd93d2c5d1707015f7d1c628de Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 26 Dec 2008 03:47:12 +0100 Subject: [PATCH 22/25] Refactoring IV: move the (still problematic) ClassLock out of the Sync compound (no semantic change, but better notation) --- src/lib/allocationcluster.cpp | 12 +++--- src/lib/allocationcluster.hpp | 2 +- src/lib/scopedholder.hpp | 2 +- src/lib/singletonpolicies.hpp | 10 ++--- src/lib/sync.hpp | 51 +++++++++++++---------- src/lib/visitordispatcher.hpp | 6 +-- src/proc/mobject/session/defsregistry.hpp | 4 +- 7 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/lib/allocationcluster.cpp b/src/lib/allocationcluster.cpp index 4dafb7d48..7e13e1538 100644 --- a/src/lib/allocationcluster.cpp +++ b/src/lib/allocationcluster.cpp @@ -81,7 +81,7 @@ namespace lib { void AllocationCluster::MemoryManager::reset (TypeInfo info) { - Sync<>::ClassLock guard(); + ClassLock guard(); if (0 < mem_.size()) purge(); type_ = info; @@ -96,7 +96,7 @@ namespace lib { void AllocationCluster::MemoryManager::purge() { - Sync<>::ClassLock guard(); + ClassLock guard(); REQUIRE (type_.killIt, "we need a deleter function"); REQUIRE (0 < type_.allocSize, "allocation size unknown"); @@ -120,7 +120,7 @@ namespace lib { inline void* AllocationCluster::MemoryManager::allocate() { - Sync<>::ClassLock guard(); + ClassLock guard(); REQUIRE (0 < type_.allocSize); REQUIRE (top_ <= mem_.size()); @@ -140,7 +140,7 @@ namespace lib { inline void AllocationCluster::MemoryManager::commit (void* pendingAlloc) { - Sync<>::ClassLock guard(); + ClassLock guard(); REQUIRE (pendingAlloc); ASSERT (top_ < mem_.size()); @@ -175,7 +175,7 @@ namespace lib { { try { - Sync<>::ClassLock guard(); + ClassLock guard(); TRACE (memory, "shutting down AllocationCluster"); for (size_t i = typeHandlers_.size(); 0 < i; --i) @@ -214,7 +214,7 @@ namespace lib { ASSERT (0 < slot); { - Sync<>::ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? + ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? if (slot > typeHandlers_.size()) typeHandlers_.resize(slot); diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index 51200e740..6b7a4838c 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -222,7 +222,7 @@ namespace lib { static TypeInfo setup() { - Sync<>::ClassLock guard(); + ClassLock guard(); if (!id_) id_= ++maxTypeIDs; diff --git a/src/lib/scopedholder.hpp b/src/lib/scopedholder.hpp index 986ba956b..f23bffeaa 100644 --- a/src/lib/scopedholder.hpp +++ b/src/lib/scopedholder.hpp @@ -221,7 +221,7 @@ namespace lib { return created_? &_ThisType::created_ : 0; } - bool operator! () const { return !created_; } + bool operator! () const { return !created_; } friend void diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index 83c76f5ca..ce01f6ce7 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -40,10 +40,10 @@ This code is heavily inspired by #include -namespace lumiera - { - namespace singleton - { +namespace lumiera { + namespace singleton { + + /* === several Policies usable in conjunction with lumiera::Singleton === */ /** @@ -139,7 +139,7 @@ namespace lumiera struct Multithreaded { typedef volatile S VolatileType; - typedef lib::Sync<>::ClassLock Lock; + typedef lib::ClassLock Lock; }; diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index b0a9df32a..01930506e 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -284,7 +284,7 @@ namespace lib { * @todo actually implement this facility using the Lumiera backend. */ template - struct Sync + class Sync { typedef sync::Monitor Monitor; Monitor objectMonitor_; @@ -296,22 +296,8 @@ namespace lib { return forThis->objectMonitor_; } - template - static Monitor& - getMonitor() - { - //TODO: a rather obscure race condition is hidden here: - //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. - //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. - //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" - //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode - - static scoped_ptr classMonitor_ (0); - if (!classMonitor_) classMonitor_.reset (new Monitor ()); - return *classMonitor_; - } - + public: class Lock { Monitor& mon_; @@ -332,14 +318,35 @@ namespace lib { }; - template - struct ClassLock : Lock - { - ClassLock() : Lock (getMonitor()) {} - }; - }; + + template + class ClassLock + : public Sync::Lock + { + typedef typename Sync::Lock Lock; + typedef typename Sync::Monitor Monitor; + + + static Monitor& + getMonitor() + { + //TODO: a rather obscure race condition is hidden here: + //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. + //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. + //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" + //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode + + static scoped_ptr classMonitor_ (0); + if (!classMonitor_) classMonitor_.reset (new Monitor ()); + return *classMonitor_; + } + + + public: + ClassLock() : Lock (getMonitor()) {} + }; } // namespace lumiera diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 4c9145716..59784b398 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -36,7 +36,7 @@ namespace lumiera { namespace visitor { - using lib::Sync; + using lib::ClassLock; template class Tag; @@ -62,7 +62,7 @@ namespace lumiera { static void generateID (size_t& id) { - Sync<>::ClassLock guard(); + ClassLock guard(); if (!id) id = ++lastRegisteredID; } @@ -138,7 +138,7 @@ namespace lumiera { void accomodate (size_t index) { - Sync<>::ClassLock guard(); + ClassLock guard(); if (index > table_.size()) table_.resize (index); // performance bottleneck?? TODO: measure the real impact! } diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index f377c3fe6..3fc37e5d7 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -58,7 +58,7 @@ namespace mobject { using lumiera::P; using lumiera::Query; - using lib::Sync; + using lib::ClassLock; using std::tr1::weak_ptr; using std::string; @@ -162,7 +162,7 @@ namespace mobject { static void createSlot (Table& table) { - Sync<>::ClassLock guard(); + ClassLock guard(); if (!index) index = ++maxSlots; if (index > table.size()) From 90150b1350b47a9875fb59ceb6070ba0748accd5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 26 Dec 2008 03:47:49 +0100 Subject: [PATCH 23/25] tie AssetManager locking to the Asset DB impl object --- src/proc/asset/db.hpp | 12 +++++++++--- src/proc/assetmanager.cpp | 8 ++++---- src/proc/assetmanager.hpp | 6 +++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/proc/asset/db.hpp b/src/proc/asset/db.hpp index 136a9e4c9..fedbab7ea 100644 --- a/src/proc/asset/db.hpp +++ b/src/proc/asset/db.hpp @@ -27,6 +27,7 @@ #include "pre_a.hpp" +#include "lib/sync.hpp" #include "proc/asset.hpp" #include "include/error.hpp" @@ -35,11 +36,14 @@ #include -namespace asset - { +namespace asset { + using std::tr1::static_pointer_cast; using std::tr1::dynamic_pointer_cast; + using lib::Sync; + using lib::RecursiveLock_NoWait; + /* ===== hash implementations ===== */ @@ -83,7 +87,9 @@ namespace asset * instances known to the Asset Manager subsystem. * As of 8/2007 implemented by a hashtable. */ - class DB : private boost::noncopyable + class DB + : private boost::noncopyable, + public Sync { IdHashtable table; diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 939162103..0aaa26c0e 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -42,8 +42,7 @@ using lumiera::Singleton; using lib::Sync; -namespace asset - { +namespace asset { /** * AssetManager error responses, caused by querying @@ -113,14 +112,15 @@ namespace asset throw(lumiera::error::Invalid) { AssetManager& _aMang (AssetManager::instance()); + DB& registry (_aMang.registry); TODO ("check validity of Ident Category"); ID asset_id (getID (idi)); - Sync<>::ClassLock guard(); + DB::Lock guard(®istry); TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); - _aMang.registry.put (asset_id, smart_ptr); + registry.put (asset_id, smart_ptr); return asset_id; } diff --git a/src/proc/assetmanager.hpp b/src/proc/assetmanager.hpp index 44bb5afc0..30962ed02 100644 --- a/src/proc/assetmanager.hpp +++ b/src/proc/assetmanager.hpp @@ -53,8 +53,7 @@ using std::list; -namespace asset - { +namespace asset { class DB; @@ -62,7 +61,8 @@ namespace asset /** * Facade for the Asset subsystem */ - class AssetManager : private boost::noncopyable + class AssetManager + : private boost::noncopyable { asset::DB & registry; From 2173698e75de20971139a3edc9fcaa8632598fff Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 26 Dec 2008 04:25:01 +0100 Subject: [PATCH 24/25] splitt off the (somewhat problematic) class locking case into a separate header --- src/lib/allocationcluster.hpp | 2 +- src/lib/singletonpolicies.hpp | 2 +- src/lib/sync-classlock.hpp | 71 +++++++++++++++++++++++ src/lib/sync.hpp | 30 +--------- src/lib/visitordispatcher.hpp | 2 +- src/proc/mobject/session/defsregistry.hpp | 2 +- 6 files changed, 77 insertions(+), 32 deletions(-) create mode 100644 src/lib/sync-classlock.hpp diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index 6b7a4838c..e12ccf174 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -52,7 +52,7 @@ #include #include "include/error.hpp" -#include "lib/sync.hpp" +#include "lib/sync-classlock.hpp" #include "lib/scopedholder.hpp" #include "lib/scopedholdertransfer.hpp" diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index ce01f6ce7..d66182f6a 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -34,7 +34,7 @@ This code is heavily inspired by #ifndef LUMIERA_SINGLETONPOLICIES_H #define LUMIERA_SINGLETONPOLICIES_H -#include "lib/sync.hpp" +#include "lib/sync-classlock.hpp" #include "include/error.hpp" #include diff --git a/src/lib/sync-classlock.hpp b/src/lib/sync-classlock.hpp new file mode 100644 index 000000000..991291806 --- /dev/null +++ b/src/lib/sync-classlock.hpp @@ -0,0 +1,71 @@ +/* + SYNC-CLASSLOCK.hpp - special case of object based locking tied directly to a type + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file sync-classlock.hpp + ** A special implementation of lib::Sync, where the storage of the object monitor + ** is associated directly to a type rather then to a single object instance. While + ** being problematic in conjunction with static startup/shutdown, doing so is sometimes + ** necessary to setup type based dispatcher tables, managing singleton creation etc. + ** + ** @see singletonfactory.hpp usage example + */ + + +#ifndef LIB_SYNC_CLASSLOCK_H +#define LIB_SYNC_CLASSLOCK_H + +#include "lib/sync.hpp" + + +namespace lib { + + template + class ClassLock + : public Sync::Lock + { + typedef typename Sync::Lock Lock; + typedef typename Sync::Monitor Monitor; + + + static Monitor& + getMonitor() + { + //TODO: a rather obscure race condition is hidden here: + //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. + //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. + //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" + //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode + + static scoped_ptr classMonitor_ (0); + if (!classMonitor_) classMonitor_.reset (new Monitor ()); + return *classMonitor_; + } + + + public: + ClassLock() : Lock (getMonitor()) {} + }; + + +} // namespace lib +#endif diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 01930506e..81b2ec019 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -1,5 +1,5 @@ /* - CONCURRENCY.hpp - generic helper for object based locking and synchronisation + SYNC.hpp - generic helper for object based locking and synchronisation Copyright (C) Lumiera.org 2008, Christian Thaeter @@ -320,33 +320,7 @@ namespace lib { }; - - template - class ClassLock - : public Sync::Lock - { - typedef typename Sync::Lock Lock; - typedef typename Sync::Monitor Monitor; - - - static Monitor& - getMonitor() - { - //TODO: a rather obscure race condition is hidden here: - //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. - //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. - //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" - //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode - - static scoped_ptr classMonitor_ (0); - if (!classMonitor_) classMonitor_.reset (new Monitor ()); - return *classMonitor_; - } - - - public: - ClassLock() : Lock (getMonitor()) {} - }; + } // namespace lumiera diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 59784b398..65b2ecfff 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -26,7 +26,7 @@ #define LUMIERA_VISITORDISPATCHER_H #include "include/error.hpp" -#include "lib/sync.hpp" +#include "lib/sync-classlock.hpp" #include "lib/singleton.hpp" #include "lib/util.hpp" diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 3fc37e5d7..06133b292 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -40,7 +40,7 @@ #define MOBJECT_SESSION_DEFSREGISTRY_H -#include "lib/sync.hpp" +#include "lib/sync-classlock.hpp" #include "lib/query.hpp" #include "lib/util.hpp" #include "lib/p.hpp" From bd2ead37d591f495fc5c94449362c193262a4d97 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 26 Dec 2008 05:44:49 +0100 Subject: [PATCH 25/25] Refactoring V: get lifecycle of a class-based monitor correct. --- src/lib/sync-classlock.hpp | 74 ++++++++++++++++++----- tests/40components.tests | 20 +++++-- tests/lib/sync-classlock-test.cpp | 98 +++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 tests/lib/sync-classlock-test.cpp diff --git a/src/lib/sync-classlock.hpp b/src/lib/sync-classlock.hpp index 991291806..2cb37d17a 100644 --- a/src/lib/sync-classlock.hpp +++ b/src/lib/sync-classlock.hpp @@ -27,6 +27,8 @@ ** being problematic in conjunction with static startup/shutdown, doing so is sometimes ** necessary to setup type based dispatcher tables, managing singleton creation etc. ** + ** @note simply using the ClassLock may cause a Monitor object (with a mutex) to be + ** created at static initialisation and destroyed on application shutdown. ** @see singletonfactory.hpp usage example */ @@ -39,31 +41,73 @@ namespace lib { + namespace { // implementation details + + template + struct NiftyHolder + { + static uint accessed_; + static char content_[sizeof(X)]; + + NiftyHolder() + { + if (!accessed_) + new(content_) X(); + ++accessed_; + } + + ~NiftyHolder() + { + --accessed_; + if (0==accessed_) + get().~X(); + } + + X& + get() + { + X* obj = reinterpret_cast (&content_); + ASSERT (obj, "Logic of Schwartz counter broken."); + return *obj; + } + }; + + template + uint NiftyHolder::accessed_; + + template + char NiftyHolder::content_[sizeof(X)]; + + } // (End) implementation details + + + + /** + * A synchronisation protection guard employing a lock scoped + * to the parameter type as a whole, not an individual instance. + * After creating an instance, every other access specifying the same + * type is blocked. + * @warn beware of recursion when using a nonrecursive Mutex + * @see Sync::Lock the usual simple instance-bound variant + */ template class ClassLock : public Sync::Lock { typedef typename Sync::Lock Lock; - typedef typename Sync::Monitor Monitor; + typedef typename sync::Monitor Monitor; - - static Monitor& - getMonitor() + Monitor& + getPerClassMonitor() { - //TODO: a rather obscure race condition is hidden here: - //TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock. - //TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex. - //TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!" - //TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode - - static scoped_ptr classMonitor_ (0); - if (!classMonitor_) classMonitor_.reset (new Monitor ()); - return *classMonitor_; + static NiftyHolder __used_here; + return __used_here.get(); } - public: - ClassLock() : Lock (getMonitor()) {} + ClassLock() : Lock (getPerClassMonitor()) {} + + uint use_count() { return NiftyHolder::accessed_; } }; diff --git a/tests/40components.tests b/tests/40components.tests index c1bf07b0c..ca079d7ef 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -158,11 +158,6 @@ return: 0 END -PLANNED "Multithread Locking by Monitor" ConcurrencyLocking_test < Testgroup=ALL diff --git a/tests/lib/sync-classlock-test.cpp b/tests/lib/sync-classlock-test.cpp new file mode 100644 index 000000000..d53c031ef --- /dev/null +++ b/tests/lib/sync-classlock-test.cpp @@ -0,0 +1,98 @@ +/* + SyncClasslock(Test) - validate the type-based Monitor locking + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "include/error.hpp" + +#include "lib/sync-classlock.hpp" + +//#include + +//using std::cout; +using test::Test; + + +namespace lib { + namespace test { + + namespace { // private test classes and data... + + const uint NUM_INSTANCES = 20; ///< number of probe instances to create + + + /** + * instances of this probe class will be created statically. + * They utilise the class-based locking within ctor and dtor + */ + struct Probe + { + Probe() { ClassLock ctor_lock; } + ~Probe() { ClassLock dtor_lock; } + }; + + + } // (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) + { + static Probe objs[NUM_INSTANCES]; + + ClassLock get_class_lock; + ASSERT ( 1 == get_class_lock.use_count()); // ClassLock got created exactly once + } + + }; + + + + /** Register this test class... */ + LAUNCHER (SyncClasslock_test, "unit common"); + + + + } // namespace test + +} // namespace lumiera