Merge object monitor locking
This commit is contained in:
commit
93c4a282cc
25 changed files with 1038 additions and 153 deletions
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace lumiera {
|
|||
bool
|
||||
Subsys::isRunning()
|
||||
{
|
||||
//Lock guard (*this);
|
||||
//Lock guard (this);
|
||||
return checkRunningState();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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); \
|
||||
} \
|
||||
}))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ namespace lib {
|
|||
return created_? &_ThisType::created_ : 0;
|
||||
}
|
||||
|
||||
bool operator! () const { return !created_; }
|
||||
bool operator! () const { return !created_; }
|
||||
|
||||
|
||||
friend void
|
||||
|
|
|
|||
|
|
@ -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
115
src/lib/sync-classlock.hpp
Normal 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
327
src/lib/sync.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(®istry);
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
98
tests/lib/sync-classlock-test.cpp
Normal file
98
tests/lib/sync-classlock-test.cpp
Normal 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
|
||||
207
tests/lib/sync-locking-test.cpp
Normal file
207
tests/lib/sync-locking-test.cpp
Normal 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
|
||||
164
tests/lib/sync-waiting-test.cpp
Normal file
164
tests/lib/sync-waiting-test.cpp
Normal 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
|
||||
Loading…
Reference in a new issue