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
* * @ see concurrency - locking - test . cpp
* * @ see asset : : AssetManager : : reg ( ) usage example
* * @ see subsystemrunner . hpp usage example
*/
2007-09-16 03:02:05 +02:00
2008-12-15 05:20:30 +01:00
# ifndef LIB_CONCURRENCY_H
# define LIB_CONCURRENCY_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 {
2007-09-16 03:02:05 +02:00
2008-12-22 07:23:48 +01:00
class RecMutex
{
lumiera_mutex mtx_ ;
2008-12-23 00:03:04 +01:00
pthread_mutex_t * get ( ) { return & mtx_ . mutex ; }
friend class Condition ;
2008-12-22 07:23:48 +01:00
public :
2008-12-23 00:03:04 +01:00
RecMutex ( ) { lumiera_recmutex_init ( & mtx_ , " Obj.Monitor RecMutex " , & NOBUG_FLAG ( sync ) ) ; }
~ RecMutex ( ) { lumiera_mutex_destroy ( & mtx_ , & NOBUG_FLAG ( sync ) ) ; }
2008-12-22 07:23:48 +01:00
2008-12-23 00:03:04 +01:00
void
acquire ( )
2008-12-22 07:23:48 +01:00
{
TODO ( " Record we may block on mutex " ) ;
2008-12-23 00:03:04 +01:00
if ( pthread_mutex_lock ( get ( ) ) )
2008-12-22 07:23:48 +01:00
throw lumiera : : error : : State ( " Mutex acquire failed. " ) ; ///////TODO capture the error-code
TODO ( " Record we successfully acquired the mutex " ) ;
}
2008-12-23 00:03:04 +01:00
void
release ( )
2008-12-22 07:23:48 +01:00
{
TODO ( " Record we are releasing the mutex " ) ;
2008-12-23 00:03:04 +01:00
pthread_mutex_unlock ( get ( ) ) ;
2008-12-22 07:23:48 +01:00
}
2008-12-23 00:03:04 +01:00
} ;
class Condition
{
lumiera_condition cond_ ;
public :
2008-12-23 04:27:11 +01:00
Condition ( ) { lumiera_condition_init ( & cond_ , " Obj.Monitor Condition " , & NOBUG_FLAG ( sync ) ) ; }
2008-12-23 00:03:04 +01:00
~ Condition ( ) { lumiera_condition_destroy ( & cond_ , & NOBUG_FLAG ( sync ) ) ; }
void
signal ( bool wakeAll = false )
{
if ( wakeAll )
pthread_cond_broadcast ( & cond_ . cond ) ;
else
pthread_cond_signal ( & cond_ . cond ) ;
}
2008-12-23 04:27:11 +01:00
2008-12-23 00:03:04 +01:00
template < class BF >
bool
2008-12-23 04:27:11 +01:00
wait ( BF & predicate , RecMutex & mtx , timespec * waitEndTime = 0 )
2008-12-23 00:03:04 +01:00
{
int err = 0 ;
while ( ! predicate ( ) & & ! err )
if ( waitEndTime )
err = pthread_cond_timedwait ( & cond_ . cond , mtx . get ( ) , waitEndTime ) ;
else
err = pthread_cond_wait ( & cond_ . cond , mtx . get ( ) ) ;
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-23 04:27:11 +01:00
struct Monitor
{
sync : : RecMutex mtx_ ;
sync : : Condition cond_ ;
Monitor ( ) { }
~ Monitor ( ) { }
void acquireLock ( ) { mtx_ . acquire ( ) ; }
void releaseLock ( ) { mtx_ . release ( ) ; }
void signal ( bool a ) { cond_ . signal ( a ) ; }
inline bool wait ( volatile bool & , ulong ) ;
} ;
typedef volatile bool & Flag ;
struct BoolFlagPredicate
{
Flag flag_ ;
BoolFlagPredicate ( Flag f ) : flag_ ( f ) { }
bool operator ( ) ( ) { return flag_ ; }
} ;
bool
Monitor : : wait ( Flag flag , ulong timeoout )
{
BoolFlagPredicate checkFlag ( flag ) ;
return cond_ . wait ( checkFlag , mtx_ , ( timespec * ) 0 ) ;
}
2008-12-22 07:23:48 +01:00
} // namespace sync
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 ) ; }
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-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