Merge object monitor locking

This commit is contained in:
Fischlurch 2008-12-27 01:04:20 +01:00
commit 93c4a282cc
25 changed files with 1038 additions and 153 deletions

View file

@ -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')

View file

@ -53,7 +53,7 @@ namespace lumiera {
bool
Subsys::isRunning()
{
//Lock guard (*this);
//Lock guard (this);
return checkRunningState();
}

View file

@ -27,7 +27,7 @@
#include "lib/error.hpp"
#include "lib/util.hpp"
#include "common/subsys.hpp"
#include "lib/multithread.hpp"
#include "lib/sync.hpp"
#include <tr1/functional>
#include <vector>
@ -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
}

View file

@ -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);

View file

@ -71,7 +71,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)/sync.hpp \
$(liblumiera_la_srcdir)/p.hpp \
$(liblumiera_la_srcdir)/query.hpp \
$(liblumiera_la_srcdir)/singletonfactory.hpp \

View file

@ -81,7 +81,7 @@ namespace lib {
void
AllocationCluster::MemoryManager::reset (TypeInfo info)
{
Thread::Lock<MemoryManager> guard SIDEEFFECT;
ClassLock<MemoryManager> guard();
if (0 < mem_.size()) purge();
type_ = info;
@ -96,7 +96,7 @@ namespace lib {
void
AllocationCluster::MemoryManager::purge()
{
Thread::Lock<MemoryManager> guard SIDEEFFECT;
ClassLock<MemoryManager> 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()
{
Thread::Lock<MemoryManager> guard SIDEEFFECT;
ClassLock<MemoryManager> guard();
REQUIRE (0 < type_.allocSize);
REQUIRE (top_ <= mem_.size());
@ -140,7 +140,7 @@ namespace lib {
inline void
AllocationCluster::MemoryManager::commit (void* pendingAlloc)
{
Thread::Lock<MemoryManager> guard SIDEEFFECT;
ClassLock<MemoryManager> guard();
REQUIRE (pendingAlloc);
ASSERT (top_ < mem_.size());
@ -175,7 +175,7 @@ namespace lib {
{
try
{
Thread::Lock<AllocationCluster> guard SIDEEFFECT
ClassLock<AllocationCluster> guard();
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<AllocationCluster> guard SIDEEFFECT; /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class?
ClassLock<AllocationCluster> guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class?
if (slot > typeHandlers_.size())
typeHandlers_.resize(slot);

View file

@ -51,17 +51,17 @@
#include <boost/scoped_ptr.hpp>
#include <boost/noncopyable.hpp>
#include "lib/multithread.hpp"
#include "lib/error.hpp"
#include "lib/sync-classlock.hpp"
#include "lib/scopedholder.hpp"
#include "lib/scopedholdertransfer.hpp"
namespace lib {
using boost::scoped_ptr;
using lumiera::Thread;
/**
* A pile of objects sharing common allocation and lifecycle.
* AllocationCluster owns a number of object families of various types.
@ -223,7 +223,7 @@ namespace lib {
static TypeInfo
setup()
{
Thread::Lock<AllocationCluster> guard SIDEEFFECT;
ClassLock<AllocationCluster> guard();
if (!id_)
id_= ++maxTypeIDs;

View file

@ -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 \

View file

@ -1,57 +0,0 @@
/*
MULTITHREAD.hpp - generic interface for multithreading primitives
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
Hermann Vosseler <Ichthyostega@web.de>
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.
*/
#ifndef LUMIERA_MULTITHREAD_H
#define LUMIERA_MULTITHREAD_H
#include "include/nobugcfg.h"
#include "lib/util.hpp"
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.
*
* @todo actually implement this policy using the Lumiera databackend.
*/
struct Thread
{
template<class X>
class Lock
{
public:
Lock() { TODO ("aquire Thread Lock for Class"); }
Lock(X*) { TODO ("aquire Thread Lock for Instance"); }
~Lock() { TODO ("release Thread Lock"); }
};
};
} // namespace lumiera
#endif

View file

@ -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); \
} \
}))

View file

@ -221,7 +221,7 @@ namespace lib {
return created_? &_ThisType::created_ : 0;
}
bool operator! () const { return !created_; }
bool operator! () const { return !created_; }
friend void

View file

@ -34,16 +34,16 @@ This code is heavily inspired by
#ifndef LUMIERA_SINGLETONPOLICIES_H
#define LUMIERA_SINGLETONPOLICIES_H
#include "lib/multithread.hpp"
#include "lib/error.hpp"
#include "lib/sync-classlock.hpp"
#include <vector>
namespace lumiera
{
namespace singleton
{
namespace lumiera {
namespace singleton {
/* === several Policies usable in conjunction with lumiera::Singleton === */
/**
@ -77,11 +77,11 @@ namespace lumiera
static S* create () { return new S; }
static void destroy (S* pS) { delete pS; }
};
typedef void (*DelFunc)(void);
using std::vector;
@ -139,7 +139,7 @@ namespace lumiera
struct Multithreaded
{
typedef volatile S VolatileType;
typedef lumiera::Thread::Lock<S> Lock;
typedef lib::ClassLock<S> Lock;
};
@ -152,7 +152,7 @@ namespace lumiera
typedef S VolatileType;
struct Lock {};
};
} // namespace singleton

115
src/lib/sync-classlock.hpp Normal file
View file

@ -0,0 +1,115 @@
/*
SYNC-CLASSLOCK.hpp - special case of object based locking tied directly to a type
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
Hermann Vosseler <Ichthyostega@web.de>
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.
**
** @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
*/
#ifndef LIB_SYNC_CLASSLOCK_H
#define LIB_SYNC_CLASSLOCK_H
#include "lib/sync.hpp"
namespace lib {
namespace { // implementation details
template<class X>
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<X*> (&content_);
ASSERT (obj, "Logic of Schwartz counter broken.");
return *obj;
}
};
template<class X>
uint NiftyHolder<X>::accessed_;
template<class X>
char NiftyHolder<X>::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 X, class CONF = NonrecursiveLock_NoWait>
class ClassLock
: public Sync<CONF>::Lock
{
typedef typename Sync<CONF>::Lock Lock;
typedef typename sync::Monitor<CONF> Monitor;
Monitor&
getPerClassMonitor()
{
static NiftyHolder<Monitor> __used_here;
return __used_here.get();
}
public:
ClassLock() : Lock (getPerClassMonitor()) {}
uint use_count() { return NiftyHolder<Monitor>::accessed_; }
};
} // namespace lib
#endif

327
src/lib/sync.hpp Normal file
View file

@ -0,0 +1,327 @@
/*
SYNC.hpp - generic helper for object based locking and synchronisation
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
Hermann Vosseler <Ichthyostega@web.de>
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.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 sync-locking-test.cpp
** @see sync-waiting-test.cpp
** @see asset::AssetManager::reg() usage example
** @see subsystemrunner.hpp usage example
*/
#ifndef LIB_SYNC_H
#define LIB_SYNC_H
#include "include/nobugcfg.h"
#include "lib/util.hpp"
#include "include/error.hpp"
extern "C" {
#include "lib/mutex.h"
#include "lib/condition.h"
}
#include <boost/scoped_ptr.hpp>
#include <cerrno>
#include <ctime>
namespace lib {
using boost::scoped_ptr;
/** Helpers and building blocks for Monitor based synchronisation */
namespace sync {
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 MTX>
class Mutex
: protected MTX
{
protected:
using MTX::mutex;
using MTX::__may_block;
using MTX::__enter;
using MTX::__leave;
public:
void
acquire()
{
__may_block();
if (pthread_mutex_lock (&mutex))
throw lumiera::error::State("Mutex acquire failed."); ///////TODO capture the error-code
__enter();
}
void
release()
{
__leave();
pthread_mutex_unlock (&mutex);
}
};
class Timeout;
template<class CDX>
class Condition
: public Mutex<CDX>
{
protected:
using CDX::cond;
using CDX::mutex;
public:
void
signal (bool wakeAll=false)
{
if (wakeAll)
pthread_cond_broadcast (&cond);
else
pthread_cond_signal (&cond);
}
template<class BF>
bool
wait (BF& predicate, Timeout& waitEndTime)
{
int err=0;
while (!predicate() && !err)
if (waitEndTime)
err = pthread_cond_timedwait (&cond, &mutex, &waitEndTime);
else
err = pthread_cond_wait (&cond, &mutex);
if (!err) return true;
if (ETIMEDOUT==err) return false;
throw lumiera::error::State ("Condition wait failed."); ///////////TODO extract error-code
}
};
/** 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
{
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;
struct BoolFlagPredicate
{
Flag flag_;
BoolFlagPredicate (Flag f) : flag_(f) {}
bool operator() () { return flag_; }
};
template<class IMPL>
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_);}
};
typedef Mutex<Wrapped_LumieraExcMutex> NonrecursiveLock_NoWait;
typedef Mutex<Wrapped_LumieraRecMutex> RecursiveLock_NoWait;
typedef Condition<Wrapped_LumieraExcCond> NonrecursiveLock_Waitable;
typedef Condition<Wrapped_LumieraRecCond> 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.
* 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 facility using the Lumiera backend.
*/
template<class CONF = NonrecursiveLock_NoWait>
class Sync
{
typedef sync::Monitor<CONF> Monitor;
Monitor objectMonitor_;
static Monitor&
getMonitor(Sync* forThis)
{
REQUIRE (forThis);
return forThis->objectMonitor_;
}
public:
class Lock
{
Monitor& mon_;
public:
template<class X>
Lock(X* it) : mon_(getMonitor(it)){ mon_.acquireLock(); }
Lock(Monitor& m) : mon_(m) { mon_.acquireLock(); }
~Lock() { mon_.releaseLock(); }
template<typename C>
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);}
};
};
} // namespace lumiera
#endif

View file

@ -27,17 +27,19 @@
#include "lib/error.hpp"
#include "lib/util.hpp"
#include "lib/sync-classlock.hpp"
#include "lib/singleton.hpp"
#include "lib/multithread.hpp"
#include "lib/util.hpp"
#include <vector>
namespace lumiera
{
namespace visitor
{
namespace lumiera {
namespace visitor {
using lib::ClassLock;
template<class TOOL> class Tag;
@ -62,7 +64,7 @@ namespace lumiera
static void
generateID (size_t& id)
{
Thread::Lock<Tag> guard SIDEEFFECT;
ClassLock<Tag> guard();
if (!id)
id = ++lastRegisteredID;
}
@ -88,10 +90,10 @@ namespace lumiera
/** storage for the Tag registry for each concrete tool */
template<class TOOL, class TOOLImpl>
Tag<TOOL> TagTypeRegistry<TOOL,TOOLImpl>::tag;
template<class TOOL>
size_t Tag<TOOL>::lastRegisteredID (0);
@ -129,7 +131,7 @@ namespace lumiera
}
typedef ReturnType (*Trampoline) (TAR&, TOOL& );
/** VTable for storing the Trampoline pointers */
std::vector<Trampoline> table_;
@ -138,7 +140,7 @@ namespace lumiera
void
accomodate (size_t index)
{
Thread::Lock<Dispatcher> guard SIDEEFFECT;
ClassLock<Dispatcher> guard();
if (index > table_.size())
table_.resize (index); // performance bottleneck?? TODO: measure the real impact!
}
@ -173,7 +175,7 @@ namespace lumiera
{
return tool.onUnknown (target);
}
public:
static Singleton<Dispatcher<TAR,TOOL> > instance;
@ -201,11 +203,11 @@ namespace lumiera
}
};
/** storage for the dispatcher table(s) */
template<class TAR, class TOOL>
Singleton<Dispatcher<TAR,TOOL> > Dispatcher<TAR,TOOL>::instance;

View file

@ -27,6 +27,7 @@
#include "pre_a.hpp"
#include "lib/sync.hpp"
#include "proc/asset.hpp"
#include "lib/error.hpp"
@ -35,11 +36,14 @@
#include <boost/utility.hpp>
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<RecursiveLock_NoWait>
{
IdHashtable table;

View file

@ -24,7 +24,7 @@
#include "proc/assetmanager.hpp"
#include "proc/asset/db.hpp"
#include "lib/multithread.hpp"
#include "lib/sync.hpp"
#include "lib/util.hpp"
#include <boost/function.hpp>
@ -39,11 +39,10 @@ using boost::bind;
using util::for_each;
using lumiera::Singleton;
using lumiera::Thread;
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<KIND> asset_id (getID (idi));
Thread::Lock<DB> guard SIDEEFFECT;
DB::Lock guard(&registry);
TODO ("handle duplicate Registrations");
P<KIND> smart_ptr (obj, &destroy);
_aMang.registry.put (asset_id, smart_ptr);
registry.put (asset_id, smart_ptr);
return asset_id;
}
@ -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<Asset>), 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<class KIND>
P<KIND>
@ -197,7 +197,7 @@ namespace asset
/**
* remove the given asset from the internal DB
* <i>together with all its dependants</i>
* <i>together with all its dependents</i>
*/
void
AssetManager::remove (IDA id)

View file

@ -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;

View file

@ -40,7 +40,7 @@
#define MOBJECT_SESSION_DEFSREGISTRY_H
#include "lib/multithread.hpp"
#include "lib/sync-classlock.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
#include "lib/p.hpp"
@ -53,13 +53,12 @@
#include <boost/lambda/lambda.hpp>
namespace mobject
{
namespace session
{
namespace mobject {
namespace session {
using lumiera::P;
using lumiera::Query;
using lumiera::Thread;
using lib::ClassLock;
using std::tr1::weak_ptr;
using std::string;
@ -163,7 +162,7 @@ namespace mobject
static void
createSlot (Table& table)
{
Thread::Lock<TableEntry> guard SIDEEFFECT;
ClassLock<TableEntry> guard();
if (!index)
index = ++maxSlots;
if (index > table.size())

View file

@ -317,6 +317,21 @@ PLANNED "StreamTypeLifecycle_test" StreamTypeLifecycle_test <<END
END
TEST "Multithread Locking by Monitor" SyncLocking_test <<END
return: 0
END
TEST "Multithread Locking whole class" SyncClasslock_test <<END
return: 0
END
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
return: 0
END
TEST "TestOption_test" TestOption_test <<END
out: Testing invocation with cmdline: ...
out: --> Testgroup=ALL

View file

@ -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 \

View file

@ -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;

View file

@ -0,0 +1,98 @@
/*
SyncClasslock(Test) - validate the type-based Monitor locking
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
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 <iostream>
//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<Probe> ctor_lock; }
~Probe() { ClassLock<Probe> 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<Probe> get_class_lock;
ASSERT ( 1 == get_class_lock.use_count()); // ClassLock<Probe> got created exactly once
}
};
/** Register this test class... */
LAUNCHER (SyncClasslock_test, "unit common");
} // namespace test
} // namespace lumiera

View file

@ -0,0 +1,207 @@
/*
SyncLocking(Test) - check the monitor object based locking
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
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 <glibmm.h>
#include <iostream>
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
: public Sync<RecursiveLock_NoWait>
{
volatile long cnt_[NUM_COUNTERS];
volatile uint step_; ///< @note stored as instance variable
void
pause ()
{
Lock guard (this); // note recursive lock
for ( uint i=0, lim=(rand() % MAX_PAUSE); i<lim; ++i);
}
void
incrementAll ()
{
for (uint i=0; i<NUM_COUNTERS; ++i)
{
pause();
cnt_[i] += step_;
}
}
public:
Victim ()
{
for (uint i=0; i<NUM_COUNTERS; ++i)
cnt_[i] = 0;
}
void
inc (uint newStep)
{
Lock guard (this);
step_ = newStep;
incrementAll();
}
bool
belowLimit ()
{
Lock guard (this);
return cnt_[0] < MAX_SUM;
}
bool
checkAllEqual ()
{
for (uint i=1; i<NUM_COUNTERS; ++i)
if (cnt_[i-1] != cnt_[i])
return false;
return true;
}
void
report ()
{
for (uint i=0; i<NUM_COUNTERS; ++i)
cout << "Counter-#" << i << " = " << cnt_[i] << "\n";
}
}
ourVictim;
/**
* A Thread trying to increment all victim counters in sync...
*/
class HavocThread
{
Glib::Thread * thread_;
void
doIt ()
{
while (ourVictim.belowLimit())
ourVictim.inc (rand() % MAX_INC);
}
public:
HavocThread ()
: thread_(0)
{ }
~HavocThread ()
{
if (thread_)
thread_->join();
}
void
start ()
{
thread_ = Glib::Thread::create(sigc::mem_fun(*this, &HavocThread::doIt), true);
ASSERT (thread_);
}
};
} // (End) test classes and data....
/**********************************************************************
* @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 sync.hpp
*/
class SyncLocking_test : public Test
{
virtual void
run (Arg)
{
if (!Glib::thread_supported())
Glib::thread_init();
REQUIRE (ourVictim.checkAllEqual());
{
HavocThread threads[NUM_THREADS];
for (uint i=0; i<NUM_THREADS; ++i)
threads[i].start();
}
// all finished and joined here...
if (!ourVictim.checkAllEqual())
{
cout << "Thread locking is broken; internal state got messed up\n"
"NOTE: all counters should be equal and >=" << MAX_SUM << "\n";
ourVictim.report();
}
}
};
/** Register this test class... */
LAUNCHER (SyncLocking_test, "unit common");
} // namespace test
} // namespace lumiera

View file

@ -0,0 +1,164 @@
/*
SyncWaiting(Test) - check the monitor object based wait/notification
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
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 <glibmm.h>
#include <iostream>
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,
public Sync<NonrecursiveLock_Waitable>
{
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