2008-12-31 06:56:31 +01:00
/*
THREADWRAPPER . hpp - thin convenience wrapper for starting lumiera threads
2010-12-17 23:28:49 +01:00
2008-12-31 06:56:31 +01:00
Copyright ( C ) Lumiera . org
2011-02-05 20:56:51 +01:00
2008 , 2010 Hermann Vosseler < Ichthyostega @ web . de >
2010-01-24 14:05:32 +01:00
Christian Thaeter < ct @ pipapo . org >
2010-12-17 23:28:49 +01:00
2008-12-31 06:56:31 +01:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
2010-12-17 23:28:49 +01:00
published by the Free Software Foundation ; either version 2 of
the License , or ( at your option ) any later version .
2008-12-31 06:56:31 +01:00
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 .
2010-12-17 23:28:49 +01:00
2008-12-31 06:56:31 +01:00
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 .
2010-12-17 23:28:49 +01:00
2008-12-31 06:56:31 +01:00
*/
2016-12-15 05:31:56 +01:00
# ifndef LIB_THREAD_WRAPPER_H
# define LIB_THREAD_WRAPPER_H
2008-12-31 06:56:31 +01:00
2010-01-20 18:45:48 +01:00
# include "lib/error.hpp"
2009-01-25 00:24:42 +01:00
# include "include/logging.h"
2010-01-24 14:05:32 +01:00
# include "lib/bool-checkable.hpp"
2010-02-14 23:39:15 +01:00
# include "lib/result.hpp"
2009-01-02 08:02:27 +01:00
extern " C " {
2009-01-16 02:18:58 +01:00
# include "backend/threads.h"
2009-01-02 08:02:27 +01:00
}
2010-01-24 15:48:48 +01:00
# include "backend/threadpool-init.hpp"
2008-12-31 06:56:31 +01:00
2014-04-03 22:42:48 +02:00
# include <functional>
2008-12-31 06:56:31 +01:00
# include <boost/noncopyable.hpp>
2009-02-01 01:01:44 +01:00
namespace backend {
2009-01-02 08:02:27 +01:00
2014-04-03 22:42:48 +02:00
using std : : bind ;
using std : : function ;
2009-09-24 23:02:40 +02:00
using lib : : Literal ;
2010-02-14 23:39:15 +01:00
namespace error = lumiera : : error ;
using error : : LUMIERA_ERROR_STATE ;
using error : : LUMIERA_ERROR_EXTERNAL ;
2009-01-02 08:02:27 +01:00
2009-01-18 15:18:58 +01:00
typedef struct nobug_flag * NoBugFlag ;
2008-12-31 06:56:31 +01:00
2009-01-18 16:14:00 +01:00
2009-01-18 15:18:58 +01:00
2013-10-24 23:06:36 +02:00
/************************************************************************/ /**
2008-12-31 06:56:31 +01:00
* A thin convenience wrapper for dealing with threads ,
2010-01-24 14:05:32 +01:00
* as implemented by the threadpool in the backend ( based on pthread ) .
2008-12-31 06:56:31 +01:00
* Using this wrapper . . .
* - helps with passing data to the function executed in the new thread
2009-01-02 08:02:27 +01:00
* - allows to bind to various kinds of functions including member functions
* The new thread starts immediately within the ctor ; after returning , the new
* thread has already copied the arguments and indeed actively started to run .
2008-12-31 06:56:31 +01:00
*
2016-12-12 01:49:11 +01:00
* # Joining , cancellation and memory management
2010-02-14 23:39:15 +01:00
* In the basic version ( class Thread ) , the created thread is completely detached
* and not further controllable . There is no way to find out its execution state ,
* wait on termination or even cancel it . Client code needs to implement such
* facilities explicitly , if needed . Care has to be taken with memory management ,
* as there are no guarantees beyond the existence of the arguments bound into
* the operation functor . If the operation in the started thread needs additional
* storage , it has to manage it actively .
2008-12-31 06:56:31 +01:00
*
2010-02-14 23:39:15 +01:00
* There is an extended version ( class ThreadJoinable ) to allow at least to wait
* on the started thread ' s termination ( joining ) . Building on this it is possible
* to create a self - contained " thread in an object " ; the dtor of such an class
* must join to prevent pulling away member variables the thread function will
* continue to use .
*
2016-12-12 01:49:11 +01:00
* # failures in the thread function
2010-02-14 23:39:15 +01:00
* The operation started in the new thread is protected by a top - level catch block .
* Error states or caught exceptions can be propagated through the lumiera_error
* state flag , when using the \ c join ( ) facility . By invoking \ join ( ) . maybeThrow ( )
* on a join - able thread , exceptions can be propagated .
* @ note any errorstate or caught exception detected on termination of a standard
* async Thread is considered a violation of policy and will result in emergency
* shutdown of the whole application .
*
2016-12-12 01:49:11 +01:00
* # synchronisation barriers
2010-02-14 23:39:15 +01:00
* Lumiera threads provide a low - level synchronisation mechanism , which is used
* to secure the hand - over of additional arguments to the thread function . It
* can be used by client code , but care has to be taken to avoid getting out
* of sync . When invoking the # sync and # syncPoint functions , the caller will
* block until the counterpart has also invoked the corresponding function .
* If this doesn ' t happen , you ' ll block forever .
2008-12-31 06:56:31 +01:00
*/
2009-01-02 08:02:27 +01:00
class Thread
2010-02-14 23:39:15 +01:00
: boost : : noncopyable
2016-12-15 05:31:56 +01:00
{
protected :
typedef function < void ( void ) > Operation ;
struct ThreadStartContext
: boost : : noncopyable
{
Operation const & operation_ ;
static void
run ( void * arg )
{
REQUIRE ( arg ) ;
ThreadStartContext * ctx = reinterpret_cast < ThreadStartContext * > ( arg ) ;
Operation _doIt_ ( ctx - > operation_ ) ;
lumiera_thread_sync ( ) ; // sync point: arguments handed over
try {
_doIt_ ( ) ; // execute the actual operation in the new thread
}
catch ( std : : exception & failure )
{
if ( ! lumiera_error_peek ( ) )
LUMIERA_ERROR_SET ( sync , STATE
, failure . what ( ) ) ;
}
catch ( . . . )
{
LUMIERA_ERROR_SET_ALERT ( sync , EXTERNAL
, " Thread terminated abnormally " ) ;
}
}
public :
ThreadStartContext ( LumieraThread & handle
, Operation const & operation_to_execute
, Literal & purpose
, NoBugFlag logging_flag
, uint additionalFlags = 0
)
: operation_ ( operation_to_execute )
{
REQUIRE ( ! lumiera_error ( ) , " Error pending at thread start " ) ;
handle =
lumiera_thread_run ( LUMIERA_THREADCLASS_INTERACTIVE | additionalFlags
, & run // invoking the run helper and..
, this // passing this start context as parameter
, purpose . c ( )
, logging_flag
) ;
if ( ! handle )
throw error : : State ( " Failed to start a new Thread for \" +purpose+ \" "
, lumiera_error ( ) ) ;
// make sure the new thread had the opportunity to take the Operation
// prior to leaving and thereby possibly destroying this local context
lumiera_thread_sync_other ( handle ) ;
}
} ;
LumieraThread thread_ ;
Thread ( ) : thread_ ( 0 ) { }
public :
/** Create a new thread to execute the given operation.
* The new thread starts up synchronously , it can ' t
* be cancelled and it can ' t be joined .
* @ param purpose fixed char string used to denote the thread for diagnostics
* @ param logging_flag NoBug flag to receive diagnostics regarding the new thread
* @ param operation defining what to execute within the new thread . Any functor
* which can be bound to function < void ( void ) > . Note this functor will be
* copied onto the stack of the new thread , thus it can be transient .
*/
Thread ( Literal purpose , Operation const & operation , NoBugFlag logging_flag = & NOBUG_FLAG ( thread ) )
: thread_ ( 0 )
{
ThreadStartContext ( thread_ , operation , purpose , logging_flag ) ;
}
/** @note by design there is no possibility to find out
* just based on the thread handle if some thread is alive .
* We define our own accounting here based on the internals
* of the thread wrapper . This will break down , if you mix
* uses of the C + + wrapper with the raw C functions . */
bool
isValid ( ) const
{
return thread_ ;
}
2016-12-20 02:35:45 +01:00
////////////////////////////////////////////////////////////////////////////TICKET #1054 : API to find out if current execution is *in* this thread!
2016-12-15 05:31:56 +01:00
/** Synchronisation barrier. In the function executing in this thread
* needs to be a corresponding Thread : : syncPoint ( ) call . Blocking until
* both the caller and the thread have reached the barrier .
*/
void
sync ( )
{
REQUIRE ( isValid ( ) , " Thread terminated " ) ;
if ( ! lumiera_thread_sync_other ( thread_ ) )
lumiera : : throwOnError ( ) ;
}
/** counterpart of the synchronisation barrier, to be called from
* within the thread to be synchronised . Will block until both
* this thread and the outward partner reached the barrier .
*/
static void
syncPoint ( )
{
2016-12-22 19:35:42 +01:00
////////////////////////////////////////////////////////TICKET #1054 : consider to call safeguard here, to ensure this is called from within the thread
2016-12-15 05:31:56 +01:00
lumiera_thread_sync ( ) ;
}
} ;
2010-01-24 14:05:32 +01:00
/**
* Variant of the standard case , allowing additionally
* to join on the termination of this thread .
*/
class ThreadJoinable
2010-02-11 03:06:42 +01:00
: public lib : : BoolCheckable < ThreadJoinable
2016-12-15 05:31:56 +01:00
, Thread > // baseclass
{
public :
ThreadJoinable ( Literal purpose , Operation const & operation ,
NoBugFlag logging_flag = & NOBUG_FLAG ( thread ) )
{
ThreadStartContext ( thread_ , operation , purpose , logging_flag ,
LUMIERA_THREAD_JOINABLE ) ;
}
/** put the caller into a blocking wait until this thread has terminated.
* @ return token signalling either success or failure .
* The caller can find out by invoking ` isValid ( ) `
* or ` maybeThrow ( ) ` on this result token
*/
lib : : Result < void >
join ( )
{
if ( ! isValid ( ) )
throw error : : Logic ( " joining on an already terminated thread " ) ;
lumiera_err errorInOtherThread =
lumiera_thread_join ( thread_ ) ;
thread_ = 0 ;
if ( errorInOtherThread )
return error : : State ( " Thread terminated with error " , errorInOtherThread ) ;
else
return true ;
}
} ;
2008-12-31 06:56:31 +01:00
2009-01-02 08:02:27 +01:00
2010-01-20 18:45:48 +01:00
} // namespace backend
2016-12-15 05:31:56 +01:00
# endif /*LIB_THREAD_WRAPPER_H*/