2007-09-16 03:02:05 +02:00
/*
2008-12-15 05:05:12 +01:00
CONCURRENCY . hpp - generic helper for object based locking and synchronisation
2007-09-16 03:02:05 +02:00
2008-03-10 04:25:03 +01:00
Copyright ( C ) Lumiera . org
2008 , Christian Thaeter < ct @ pipapo . org >
2007-11-27 03:19:35 +01:00
Hermann Vosseler < Ichthyostega @ web . de >
2007-09-16 03:02:05 +02:00
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 0213 9 , USA .
*/
2008-12-22 17:00:15 +01:00
/** @file sync.hpp
2008-12-15 05:05:12 +01:00
* * 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
2008-12-24 03:31:35 +01:00
* * @ see sync - locking - test . cpp
* * @ see sync - waiting - test . cpp
2008-12-15 05:05:12 +01:00
* * @ see asset : : AssetManager : : reg ( ) usage example
* * @ see subsystemrunner . hpp usage example
*/
2007-09-16 03:02:05 +02:00
2008-12-24 03:31:35 +01:00
# ifndef LIB_SYNC_H
# define LIB_SYNC_H
2007-09-16 03:02:05 +02:00
2008-11-30 06:43:51 +01:00
# include "include/nobugcfg.h"
2008-12-17 17:53:32 +01:00
# include "lib/util.hpp"
2008-12-23 00:03:04 +01:00
# include "include/error.hpp"
2007-09-16 03:02:05 +02:00
2008-12-22 04:50:27 +01:00
extern " C " {
# include "lib/mutex.h"
# include "lib/condition.h"
}
2008-12-15 06:18:38 +01:00
# include <boost/scoped_ptr.hpp>
2008-12-23 00:03:04 +01:00
# include <cerrno>
# include <ctime>
2008-12-15 06:18:38 +01:00
2007-09-16 03:02:05 +02:00
2008-12-15 05:20:30 +01:00
namespace lib {
2008-12-15 06:18:38 +01:00
using boost : : scoped_ptr ;
2008-12-22 07:23:48 +01:00
2008-12-23 00:03:04 +01:00
/** Helpers and building blocks for Monitor based synchronisation */
namespace sync {
2008-12-24 23:23:23 +01:00
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
2008-12-22 07:23:48 +01:00
{
public :
2008-12-23 00:03:04 +01:00
void
acquire ( )
2008-12-22 07:23:48 +01:00
{
2008-12-24 23:23:23 +01:00
__may_block ( ) ;
2008-12-22 07:23:48 +01:00
2008-12-24 23:23:23 +01:00
if ( pthread_mutex_lock ( & mutex ) )
throw lumiera : : error : : State ( " Mutex acquire failed. " ) ; ///////TODO capture the error-code
__enter ( ) ;
2008-12-22 07:23:48 +01:00
}
2008-12-23 00:03:04 +01:00
void
release ( )
2008-12-22 07:23:48 +01:00
{
2008-12-24 23:23:23 +01:00
__leave ( ) ;
pthread_mutex_unlock ( & mutex ) ;
2008-12-22 07:23:48 +01:00
}
2008-12-23 00:03:04 +01:00
} ;
2008-12-24 23:23:23 +01:00
class Timeout ;
template < class CDX >
2008-12-23 00:03:04 +01:00
class Condition
2008-12-24 23:23:23 +01:00
: public Mutex < CDX >
2008-12-23 00:03:04 +01:00
{
public :
void
signal ( bool wakeAll = false )
{
if ( wakeAll )
2008-12-24 23:23:23 +01:00
pthread_cond_broadcast ( & cond ) ;
2008-12-23 00:03:04 +01:00
else
2008-12-24 23:23:23 +01:00
pthread_cond_signal ( & cond ) ;
2008-12-23 00:03:04 +01:00
}
2008-12-23 04:27:11 +01:00
2008-12-24 03:31:35 +01:00
template < class BF , class MTX >
2008-12-23 00:03:04 +01:00
bool
2008-12-24 03:31:35 +01:00
wait ( BF & predicate , MTX & mtx , Timeout & waitEndTime )
2008-12-23 00:03:04 +01:00
{
int err = 0 ;
while ( ! predicate ( ) & & ! err )
if ( waitEndTime )
2008-12-24 23:23:23 +01:00
err = pthread_cond_timedwait ( & cond , & mutex , & waitEndTime ) ;
2008-12-23 00:03:04 +01:00
else
2008-12-24 23:23:23 +01:00
err = pthread_cond_wait ( & cond , & mutex ) ;
2008-12-23 00:03:04 +01:00
if ( ! err ) return true ;
if ( ETIMEDOUT = = err ) return false ;
throw lumiera : : error : : State ( " Condition wait failed. " ) ; ///////////TODO extract error-code
}
2008-12-22 07:23:48 +01:00
} ;
2008-12-24 03:31:35 +01:00
/** 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 ;
2008-12-23 04:27:11 +01:00
2008-12-24 03:31:35 +01:00
class Monitor
2008-12-24 23:23:23 +01:00
: Condition < Wrapped_LumieraExcCond > /////////TODO: to be refactored. This is just one example, using the non-recursive condition
2008-12-23 04:27:11 +01:00
{
2008-12-24 03:31:35 +01:00
Timeout timeout_ ;
//////TODO my intention is to make two variants of the monitor, where the simple one leaves out the condition part
2008-12-23 04:27:11 +01:00
2008-12-24 03:31:35 +01:00
public :
2008-12-23 04:27:11 +01:00
Monitor ( ) { }
~ Monitor ( ) { }
2008-12-24 23:23:23 +01:00
void acquireLock ( ) { acquire ( ) ; }
void releaseLock ( ) { release ( ) ; }
2008-12-23 04:27:11 +01:00
2008-12-24 23:23:23 +01:00
void signal ( bool a ) { signal ( a ) ; }
2008-12-23 04:27:11 +01:00
2008-12-24 03:31:35 +01:00
inline bool wait ( Flag , ulong ) ;
inline void setTimeout ( ulong ) ;
inline bool isTimedWait ( ) ;
2008-12-23 04:27:11 +01:00
} ;
struct BoolFlagPredicate
{
Flag flag_ ;
BoolFlagPredicate ( Flag f ) : flag_ ( f ) { }
bool operator ( ) ( ) { return flag_ ; }
} ;
bool
2008-12-24 03:31:35 +01:00
Monitor : : wait ( Flag flag , ulong timedwait )
2008-12-23 04:27:11 +01:00
{
BoolFlagPredicate checkFlag ( flag ) ;
2008-12-24 03:31:35 +01:00
return cond_ . wait ( checkFlag , mtx_ , timeout_ . setOffset ( timedwait ) ) ;
2008-12-23 04:27:11 +01:00
}
2008-12-24 03:31:35 +01:00
void
Monitor : : setTimeout ( ulong relative ) { timeout_ . setOffset ( relative ) ; }
bool
Monitor : : isTimedWait ( ) { return ( timeout_ ) ; }
2008-12-23 04:27:11 +01:00
2008-12-24 23:23:23 +01:00
} // namespace sync (helpers and building blocks)
2008-12-22 07:23:48 +01:00
2007-09-16 03:02:05 +02:00
/**
2008-12-15 05:05:12 +01:00
* 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 .
2007-09-16 03:02:05 +02:00
*
2008-12-15 05:05:12 +01:00
* @ todo actually implement this facility using the Lumiera backend .
2007-09-16 03:02:05 +02:00
*/
2008-12-22 17:00:15 +01:00
struct Sync
2007-09-16 03:02:05 +02:00
{
2008-12-23 04:27:11 +01:00
typedef sync : : Monitor Monitor ;
2008-12-15 05:05:12 +01:00
2008-12-15 06:18:38 +01:00
Monitor objectMonitor_ ;
/////////////////////////////////////////////////////////////////////////TODO: factor out the recursive/non-recursive mutex case as policy...
2008-12-15 05:05:12 +01:00
template < class X >
2008-12-15 06:18:38 +01:00
static inline Monitor & getMonitor ( ) ;
2008-12-22 17:00:15 +01:00
static inline Monitor & getMonitor ( Sync * forThis ) ;
2008-12-15 06:18:38 +01:00
2008-12-15 05:05:12 +01:00
2007-09-16 03:02:05 +02:00
class Lock
{
2008-12-15 05:05:12 +01:00
Monitor & mon_ ;
2008-12-23 04:27:11 +01:00
2007-09-16 03:02:05 +02:00
public :
2008-12-16 05:19:31 +01:00
template < class X >
2008-12-23 04:27:11 +01:00
Lock ( X * it ) : mon_ ( getMonitor ( it ) ) { mon_ . acquireLock ( ) ; }
Lock ( Monitor & m ) : mon_ ( m ) { mon_ . acquireLock ( ) ; }
~ Lock ( ) { mon_ . releaseLock ( ) ; }
2008-12-15 06:18:38 +01:00
2008-12-23 04:27:11 +01:00
template < typename C >
bool wait ( C & cond , ulong time = 0 ) { return mon_ . wait ( cond , time ) ; }
2008-12-24 03:31:35 +01:00
void setTimeout ( ulong time ) { mon_ . setTimeout ( time ) ; }
2008-12-23 04:27:11 +01:00
void notifyAll ( ) { mon_ . signal ( true ) ; }
void notify ( ) { mon_ . signal ( false ) ; }
2008-12-15 05:05:12 +01:00
2007-09-16 03:02:05 +02:00
} ;
2008-12-16 05:19:31 +01:00
2008-12-23 04:27:11 +01:00
2008-12-16 05:19:31 +01:00
template < class X >
struct ClassLock : Lock
{
ClassLock ( ) : Lock ( getMonitor < X > ( ) ) { }
} ;
2007-09-16 03:02:05 +02:00
} ;
2008-12-16 05:19:31 +01:00
2008-12-15 05:05:12 +01:00
2008-12-22 17:00:15 +01:00
Sync : : Monitor &
Sync : : getMonitor ( Sync * forThis )
2008-12-15 06:18:38 +01:00
{
REQUIRE ( forThis ) ;
return forThis - > objectMonitor_ ;
}
2008-12-15 05:05:12 +01:00
template < class X >
2008-12-22 17:00:15 +01:00
Sync : : Monitor &
Sync : : getMonitor ( )
2008-12-15 05:05:12 +01:00
{
2008-12-22 04:50:27 +01:00
//TODO: a rather obscure race condition is hidden here:
//TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock.
//TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex.
//TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!"
2008-12-24 03:31:35 +01:00
//TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode
2008-12-15 06:18:38 +01:00
static scoped_ptr < Monitor > classMonitor_ ( 0 ) ;
if ( ! classMonitor_ ) classMonitor_ . reset ( new Monitor ( ) ) ;
return * classMonitor_ ;
2008-12-15 05:05:12 +01:00
}
2007-09-16 03:02:05 +02:00
2008-03-10 06:09:44 +01:00
} // namespace lumiera
2007-09-16 03:02:05 +02:00
# endif