This commit is contained in:
Fischlurch 2008-12-27 07:44:28 +01:00
parent 250e3ba4df
commit 16adff318d
3 changed files with 90 additions and 32 deletions

View file

@ -22,10 +22,39 @@
*/
/** @file sync.hpp
** Collection of helpers and wrappers to support dealing with concurrency issues.
** Collection of OO-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.
** in object oriented style; especially we build upon the monitor object pattern.
**
** A class becomes \em lockable by inheriting from lib::Sync with the appropriate
** parametrisation. This causes any instance to inherit a monitor member (object),
** managing a mutex and (optionally) a condition variable for waiting. The actual
** synchronisation is achieved by placing a guard object as local (stack) variable
** into a given scope (typically a member function body). This guard object of
** class lib::Sync::Lock accesses the enclosing object's monitor and automatically
** manages the locking and unlocking; optionally it may also be used for waiting
** on a condition.
**
** Please note:
** - It is important to select a suitable parametrisation of the monitor.
** This is done by specifying one of the defined policy classes.
** - Be sure to pick the recursive mutex implementation when recursive calls
** of synchronised functions can't be avoided. (performance penalty)
** - You can't use the Lock#wait and Lock#notify functions unless you pick
** a parametrisation including a condition variable.
** - The "this" pointer is fed to the ctor of the Lock guard object. Thus
** you may use any object's monitor as appropriate, especially in cases
** when adding the monitor to a given class may cause size problems.
** - For sake of completeness, this implementation provides the ability for
** timed waits. But please consider that in most cases there are better
** solutions for running an operation with given timeout by utilising the
** Lumiera scheduler. Thus use of timed waits is \b discouraged.
** - There is a special variant of the Lock guard called ClassLock, which
** can be used to lock based on a type, not an instance.
** - in DEBUG mode, the implementation includes NoBug resource tracking.
**
** @todo fully implement the NoBug resource tracking calls
**
** @see mutex.h
** @see sync-locking-test.cpp
@ -51,10 +80,10 @@ extern "C" {
#include <ctime>
namespace lib {
/** Helpers and building blocks for Monitor based synchronisation */
namespace sync {
@ -72,7 +101,7 @@ namespace lib {
void __leave() { TODO ("Record we are releasing the mutex"); }
};
struct Wrapped_LumieraRecMutex
: public lumiera_mutex
{
@ -86,7 +115,7 @@ namespace lib {
void __leave() { TODO ("Record we are releasing the mutex"); }
};
struct Wrapped_LumieraExcCond
: public lumiera_condition
{
@ -100,9 +129,9 @@ namespace lib {
void __leave() { TODO ("Record we are releasing the mutex"); }
};
struct Wrapped_LumieraRecCond
: public lumiera_condition
: public lumiera_condition //////////////////////////////////////////TODO use correct implementation here!
{
protected:
Wrapped_LumieraRecCond() { lumiera_condition_init (this, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } ////////TODO
@ -115,8 +144,11 @@ namespace lib {
};
/* ========== abstractions defining the usable synchronisation primitives ============== */
template<class MTX>
class Mutex
class Mutex
: protected MTX
{
protected:
@ -126,8 +158,8 @@ namespace lib {
using MTX::__leave;
public:
void
acquire()
void
acquire()
{
__may_block();
@ -137,8 +169,8 @@ namespace lib {
__enter();
}
void
release()
void
release()
{
__leave();
@ -161,7 +193,7 @@ namespace lib {
public:
void
signal (bool wakeAll=false)
signal (bool wakeAll=false)
{
if (wakeAll)
pthread_cond_broadcast (&cond);
@ -188,10 +220,11 @@ namespace lib {
}
};
/** 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.
/**
* helper for specifying an optional timeout for an timed wait.
* Wrapping a timespec-struct, it allows for easy initialisation
* by a given relative offset.
*/
struct Timeout
: timespec
@ -218,6 +251,10 @@ namespace lib {
operator bool() { return 0 != tv_sec; } // allows if (timeout_)....
};
/* ==== functor types for defining the waiting condition ==== */
typedef volatile bool& Flag;
struct BoolFlagPredicate
@ -227,8 +264,8 @@ namespace lib {
bool operator() () { return flag_; }
};
template<class X>
struct BoolMethodPredicate
{
@ -242,6 +279,10 @@ namespace lib {
};
/**
* Object Monitor for synchronisation and waiting.
*/
template<class IMPL>
class Monitor
: IMPL
@ -286,13 +327,24 @@ namespace lib {
/* Interface to be used by client code:
* Inherit from class Sync with a suitable Policy.
* Then use the embedded Lock class.
*/
/* ======= Policy classes ======= */
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
@ -300,8 +352,7 @@ namespace lib {
* 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
@ -335,6 +386,10 @@ namespace lib {
template<typename C>
bool wait (C& cond, ulong time=0) { return mon_.wait(cond,time);}
/** convenience shortcut:
* Locks and immediately enters wait state,
* observing a condition defined as member function.
*/
template<class X>
Lock(X* it, volatile bool (X::*method)(void))
: mon_(getMonitor(it))
@ -346,9 +401,9 @@ namespace lib {
};
} // namespace lumiera
#endif

View file

@ -166,13 +166,15 @@ namespace lib {
* But because the class Victim uses an object level monitor to
* guard the mutations, the state should remain consistent.
*
* @see SyncWaiting_test condition based wait/notify
* @see SyncClasslock_test locking a type, not an instance
* @see sync.hpp
*/
class SyncLocking_test : public Test
{
virtual void
run (Arg)
run (Arg)
{
if (!Glib::thread_supported())
Glib::thread_init();

View file

@ -26,11 +26,8 @@
#include "lib/sync.hpp"
#include <glibmm.h>
#include <glibmm.h> //////////////TODO temp solution, replace by backend!
#include <iostream>
using std::cout;
using test::Test;
@ -124,14 +121,17 @@ namespace lib {
* This test covers the second part of the monitor pattern, which builds upon
* the locking part an additionally uses an embedded condition. We provide
* several pre-configured ways of specifying the condition to wait upon.
* - check a boolean flag
* - evaluate a member function as predicate
*
* @see SyncLocking_test
* @see sync.hpp
*/
class SyncWaiting_test : public Test
{
virtual void
run (Arg)
run (Arg)
{
if (!Glib::thread_supported())
Glib::thread_init();
@ -146,7 +146,8 @@ namespace lib {
/**
* Helper actually performing the test:
* creates two threads and let them block and wait cross-wise.
* creates two threads and let them block and wait until a start value is given.
* When awakened, each thread should add the start value to a common sum field.
* @param tok object containing the monitor and condition to be tested.
*/
void