diff --git a/src/common/guifacade.cpp b/src/common/guifacade.cpp index 39404fce5..6efdfb9f3 100644 --- a/src/common/guifacade.cpp +++ b/src/common/guifacade.cpp @@ -23,6 +23,7 @@ #include "gui/guifacade.hpp" #include "include/guinotificationfacade.h" +#include "lib/sync.hpp" #include "lib/error.hpp" #include "lib/singleton.hpp" #include "lib/functorutil.hpp" @@ -42,6 +43,7 @@ namespace gui { using lumiera::Subsys; using lumiera::InstanceHandle; using util::dispatchSequenced; + using lib::Sync; @@ -79,7 +81,8 @@ namespace gui { scoped_ptr facade (0); class GuiSubsysDescriptor - : public lumiera::Subsys + : public lumiera::Subsys, + public Sync<> { operator string () const { return "Lumiera GTK GUI"; } @@ -98,7 +101,7 @@ namespace gui { bool start (lumiera::Option&, Subsys::SigTerm termination) { - //Lock guard (*this); + Lock guard (this); if (facade) return false; // already started facade.reset ( @@ -119,14 +122,14 @@ namespace gui { bool checkRunningState () throw() { - //Lock guard (*this); + Lock guard (this); return (facade); } void closeGuiModule (lumiera::Error *) { - //Lock guard (*this); + Lock guard (this); if (!facade) { WARN (operate, "Termination signal invoked, but GUI is currently closed. " diff --git a/src/common/subsys.cpp b/src/common/subsys.cpp index 2becff32b..68850f4c8 100644 --- a/src/common/subsys.cpp +++ b/src/common/subsys.cpp @@ -23,19 +23,12 @@ #include "common/subsys.hpp" -#include "lib/error.hpp" -//#include "lib/util.hpp" - - -//using util::isnil; -//using util::cStr; namespace lumiera { - Subsys::~Subsys() { } @@ -43,7 +36,6 @@ namespace lumiera { Subsys& Subsys::depends (Subsys& prereq) { - TODO ("anything else to care when defining a dependency on the prerequisite subsystem??");/////////////////////TODO prereq_.push_back(&prereq); return *this; } @@ -53,7 +45,6 @@ namespace lumiera { bool Subsys::isRunning() { - //Lock guard (this); return checkRunningState(); } diff --git a/src/common/subsys.hpp b/src/common/subsys.hpp index 4808f134b..e59d2db28 100644 --- a/src/common/subsys.hpp +++ b/src/common/subsys.hpp @@ -65,6 +65,7 @@ namespace lumiera { * Dependencies and lifecycle of a partially independent Subsystem of the Application. * Using such descriptors, AppState as activated from main() is able to pull up, * maintain and shut down the primary parts of the Application. + * @note synchronisation is up to the implementor. */ class Subsys : private noncopyable diff --git a/src/common/subsystemrunner.hpp b/src/common/subsystemrunner.hpp index 363b4c8cc..7c6762694 100644 --- a/src/common/subsystemrunner.hpp +++ b/src/common/subsystemrunner.hpp @@ -44,8 +44,16 @@ namespace lumiera { using util::and_all; using util::for_each; using util::removeall; + using lib::Sync; + using lib::RecursiveLock_Waitable; namespace { + + /** limit waiting for subsystem shutdown in case of + * an emergency shutdown to max 2 seconds */ + const uint EMERGENCYTIMEOUT = 2000; + + function isRunning() { return bind (&Subsys::isRunning, _1); @@ -76,10 +84,9 @@ namespace lumiera { * Usually, the startup process is conducted from one (main) thread, which enters * a blocking wait() after starting the subsystems. Awakened by some termination * signal from one of the subsystems, termination of any remaining subsystems - * will be triggered. The wait() function returns after shutdown of all subsystems, + * will be triggered. The #wait() function returns after shutdown of all subsystems, * signalling an emergency exit (caused by an exception) with its return value. * - * @todo implement an object monitor primitive based on a mutex and a condition. Inherit from this privately and create local instances of the embedded Lock class to activate the locking and wait/notify * @todo maybe use my refArray (see builder) to use Subsys& instead of Subsys* ?? * * @see lumiera::AppState @@ -87,7 +94,7 @@ namespace lumiera { * @see main.cpp */ class SubsystemRunner -// : Sync + : public Sync { Option& opts_; volatile bool emergency_; @@ -109,7 +116,7 @@ namespace lumiera { void maybeRun (Subsys& susy) { - //Lock guard (this); + Lock guard (this); if (!susy.isRunning() && susy.shouldStart (opts_)) triggerStartup (&susy); @@ -120,23 +127,28 @@ namespace lumiera { void shutdownAll () { - //Lock guard (this); + Lock guard (this); for_each (running_, killIt_); } + void + triggerEmergency (bool cond) + { + Lock guard (this); + emergency_ |= cond; + } + bool wait () { - //Lock(this).wait (&SubsystemRunner::allDead); + Lock wait_blocking(this, &SubsystemRunner::allDead); return isEmergencyExit(); } - bool isEmergencyExit () { return emergency_; } - void triggerEmergency (bool cond) { emergency_ |= cond; } - private: + bool isEmergencyExit () { return emergency_; } void triggerStartup (Subsys* susy) @@ -161,20 +173,24 @@ namespace lumiera { sigTerm (Subsys* susy, Error* problem) ///< called from subsystem on termination { ASSERT (susy); - //Lock guard (this); + Lock sync (this); triggerEmergency(problem); ERROR_IF (susy->isRunning(), lumiera, "Subsystem '%s' signals termination, " "without resetting running state", cStr(*susy)); removeall (running_, susy); shutdownAll(); - //guard.notify(); + sync.notify(); } - bool + volatile bool allDead () { if (isEmergencyExit()) - ; //Lock(this).setTimeout(EMERGENCYTIMEOUT); + { + Lock sync(this); + if (!sync.isTimedWait()) + sync.setTimeout(EMERGENCYTIMEOUT); + } return isnil (running_); // end wait if no running subsystem left } diff --git a/src/lib/allocationcluster.cpp b/src/lib/allocationcluster.cpp index f60fd56a8..1e210d80d 100644 --- a/src/lib/allocationcluster.cpp +++ b/src/lib/allocationcluster.cpp @@ -24,6 +24,7 @@ #include "lib/allocationcluster.hpp" #include "lib/error.hpp" #include "lib/util.hpp" +#include "lib/sync.hpp" using util::isnil; @@ -38,9 +39,9 @@ namespace lib { * successful ctor call of the object being allocated. Allocations and commits * can be assumed to come in pairs, thus if an allocation immediately follows * another one (without commit), the previous allocation can be considered - * a failure and can be dropped silently. After an allocation has succeeds + * a failure and can be dropped silently. After an allocation succeeds * (i.e. was committed), the MemoryManager is in charge for the lifecycle - * of the object within the allocated spaces and has to guarantee calling + * of the object within the allocated space and has to guarantee calling * it's dtor, either on shutdown or on explicit #purge() -- the type info * structure handed in on initialisation provides a means for invoking * the dtor without actually knowing the object's type. @@ -55,6 +56,7 @@ namespace lib { * on real-world measurements, not guessing. */ class AllocationCluster::MemoryManager + : public Sync { typedef std::vector MemTable; TypeInfo type_; @@ -81,7 +83,7 @@ namespace lib { void AllocationCluster::MemoryManager::reset (TypeInfo info) { - ClassLock guard(); + Lock instance(); if (0 < mem_.size()) purge(); type_ = info; @@ -96,7 +98,7 @@ namespace lib { void AllocationCluster::MemoryManager::purge() { - ClassLock guard(); + Lock instance(); REQUIRE (type_.killIt, "we need a deleter function"); REQUIRE (0 < type_.allocSize, "allocation size unknown"); @@ -120,7 +122,7 @@ namespace lib { inline void* AllocationCluster::MemoryManager::allocate() { - ClassLock guard(); + Lock instance(); REQUIRE (0 < type_.allocSize); REQUIRE (top_ <= mem_.size()); @@ -140,7 +142,7 @@ namespace lib { inline void AllocationCluster::MemoryManager::commit (void* pendingAlloc) { - ClassLock guard(); + Lock instance(); REQUIRE (pendingAlloc); ASSERT (top_ < mem_.size()); @@ -174,8 +176,8 @@ namespace lib { AllocationCluster::~AllocationCluster() throw() { try - { - ClassLock guard(); + { // avoiding a per-instance lock for now. + ClassLock guard(); // (see note in the class description) TRACE (memory, "shutting down AllocationCluster"); for (size_t i = typeHandlers_.size(); 0 < i; --i) @@ -213,8 +215,8 @@ namespace lib { { ASSERT (0 < slot); - { - ClassLock guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class? + { // avoiding a per-instance lock for now. + ClassLock guard(); // (see note in the class description) if (slot > typeHandlers_.size()) typeHandlers_.resize(slot); diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp index 8b639199c..a9593316e 100644 --- a/src/lib/allocationcluster.hpp +++ b/src/lib/allocationcluster.hpp @@ -76,6 +76,12 @@ namespace lib { * the object families are to be discarded. Currently * they are just purged in reverse order defined by * the first request for allocating a certain type. + * @todo should we use an per-instance lock? We can't avoid + * the class-wide lock, unless also the type-ID registration + * is done on a per-instance base. AllocationCluster is intended + * to be used within the builder, which executes in a dedicated + * thread. Thus I doubt lock contention could be a problem and + * we can avoid using a mutex per instance. Re-evaluate this! */ class AllocationCluster : boost::noncopyable diff --git a/src/lib/singletonpolicies.hpp b/src/lib/singletonpolicies.hpp index ba63d4a99..ef2342c17 100644 --- a/src/lib/singletonpolicies.hpp +++ b/src/lib/singletonpolicies.hpp @@ -133,7 +133,7 @@ namespace lumiera { /** * Policy for handling multithreaded access to the singleton instance - * @todo actually implement this policy using the Lumiera databackend. + * @todo we could consider using a single shared lock for all singleton types... */ template struct Multithreaded diff --git a/src/lib/visitordispatcher.hpp b/src/lib/visitordispatcher.hpp index 37bee9007..0eae85168 100644 --- a/src/lib/visitordispatcher.hpp +++ b/src/lib/visitordispatcher.hpp @@ -105,6 +105,8 @@ namespace lumiera { * For each possible call entry point via some subclass of the visitable hierarchy, * we maintain a dispatcher table to keep track of all concrete tool implementations * able to receive and process calls on objects of this subclass. + * @param TAR the concrete target (subclass) type within the visitable hierarchy + * @param TOOL the overall tool family (base class of all concrete tools) */ template class Dispatcher @@ -140,7 +142,7 @@ namespace lumiera { void accomodate (size_t index) { - ClassLock guard(); + ClassLock guard(); // note: this lock is also used for the singleton! 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 06133b292..3fbd4aa1f 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -25,7 +25,7 @@ ** A piece of implementation code factored out into a separate header (include). ** Only used in defsmanager.cpp and for the unit tests. We can't place it into ** a separate compilation unit, because defsmanager.cpp defines some explicit - ** template instantiaton, which cause the different Slots of the DefsrRegistry#table_ + ** template instantiation, which cause the different Slots of the DefsrRegistry#table_ ** to be filled with data and defaults for the specific Types. ** ** @see mobject::session::DefsManager @@ -80,7 +80,7 @@ namespace mobject { }; /** we maintain an independent defaults registry - * for every participating kind of objects */ + * for every participating kind of object. */ typedef std::vector< P > Table; @@ -180,8 +180,8 @@ namespace mobject { /** - * @internal Helper for organizing preconfigured default objects. - * Maintaines a collection of objects known or encountered as "default" + * @internal Helper for organising preconfigured default objects. + * Maintains a collection of objects known or encountered as "default" * for a given type. This collection is ordered by "degree of constriction", * which is implemented by counting the number of predicates in the query * used to define or identify each object. @@ -206,17 +206,17 @@ namespace mobject { II p,i,e; P next, ptr; - Iter (II from, II to) ///< just ennumerates the given range + Iter (II from, II to) ///< just enumerates the given range : p(from), i(from), e(to) { if (i!=e) ++i; // p is next to be tested, i always one ahead operator++ (); } - Iter (II match, II from, II to) ///< returns direct match first, then ennumerates + Iter (II match, II from, II to) ///< returns direct match first, then enumerates : p(match), i(from), e(to) { - operator++ (); // init to first element (or to null if emty) + operator++ (); // init to first element (or to null if empty) } P findNext () throw() @@ -233,7 +233,7 @@ namespace mobject { public: P operator* () { return ptr; } - bool hasNext () { return next || findNext(); } + bool hasNext () { return next || findNext(); } Iter operator++ (int) { Iter tmp=*this; operator++(); return tmp; } Iter& operator++ () { @@ -243,12 +243,12 @@ namespace mobject { } }; - /** find a sequence of "default" objects possibliy matching the query. + /** find a sequence of "default" objects possibly matching the query. * If there was a registration for some object of the given kind with * the \em same query, this one will be first in the sequence. Besides, * the sequence will yield all still existing registered "default" objects * of this kind, ordered ascending by "degree of constriction", i.e. starting - * with the object registered togehter with the shortest query. + * with the object registered together with the shortest query. * @return a forward input iterator yielding this sequence * @note none of the queries will be evaluated (we're just counting predicates) */ @@ -265,7 +265,7 @@ namespace mobject { typename Registry::iterator end = registry.end(); if (pos==end) - return Iter (registry.begin(), end); // just ennumerate contents + return Iter (registry.begin(), end); // just enumerate contents else return Iter (pos, registry.begin(), end); // start with direct match }