2008-12-23 08:44:30 +01:00
/*
threads . c - Manage threads
Copyright ( C ) Lumiera . org
2008 , Christian Thaeter < ct @ pipapo . org >
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 .
*/
//TODO: Support library includes//
2009-01-28 04:56:01 +01:00
# include "include/logging.h"
2009-11-23 22:40:31 +01:00
# include "lib/safeclib.h"
2008-12-23 08:44:30 +01:00
//TODO: Lumiera header includes//
2009-01-16 02:18:58 +01:00
# include "threads.h"
2008-12-23 08:44:30 +01:00
//TODO: internal/static forward declarations//
//TODO: System includes//
2008-12-29 01:50:38 +01:00
# include <pthread.h>
2010-01-18 16:24:45 +01:00
# include <time.h>
2009-11-26 00:58:15 +01:00
# include <errno.h>
2008-12-23 08:44:30 +01:00
/**
* @ file
*
*/
2009-11-25 04:25:00 +01:00
NOBUG_DEFINE_FLAG_PARENT ( threads , threads_dbg ) ; /*TODO insert a suitable/better parent flag here */
2008-12-23 08:44:30 +01:00
//code goes here//
2009-11-29 23:55:20 +01:00
# define LUMIERA_THREAD_CLASS(name) #name,
// enum string trick: expands as an array of thread class name strings
const char * lumiera_threadclass_names [ ] = {
LUMIERA_THREAD_CLASSES
} ;
# undef LUMIERA_THREAD_CLASS
2008-12-29 01:50:38 +01:00
2009-11-30 00:06:14 +01:00
# define LUMIERA_THREAD_STATE(name) #name,
const char * lumiera_threadstate_names [ ] = {
LUMIERA_THREAD_STATES
} ;
# undef LUMIERA_THREAD_STATE
2010-01-18 00:34:53 +01:00
LUMIERA_ERROR_DEFINE ( THREAD , " fatal threads initialization error " ) ;
/* thread local storage pointing back to the thread structure of each thread */
static pthread_key_t lumiera_thread_tls ;
static pthread_once_t lumiera_thread_initialized = PTHREAD_ONCE_INIT ;
static void
lumiera_thread_tls_init ( void )
{
if ( ! ! pthread_key_create ( & lumiera_thread_tls , NULL ) )
LUMIERA_DIE ( THREAD ) ; /* should never happen */
}
static void *
thread_loop ( void * thread )
2009-11-24 01:47:20 +01:00
{
2010-01-16 16:39:45 +01:00
TRACE ( threads ) ;
2010-01-16 19:53:42 +01:00
NOBUG_THREAD_ID_SET ( " worker " ) ;
2009-12-23 19:10:31 +01:00
LumieraThread t = ( LumieraThread ) thread ;
2010-01-18 00:34:53 +01:00
pthread_setspecific ( lumiera_thread_tls , t ) ;
2009-12-23 19:10:31 +01:00
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE , NULL ) ;
2009-12-24 01:54:49 +01:00
2009-12-23 19:10:31 +01:00
REQUIRE ( t , " thread does not exist " ) ;
2009-12-24 01:54:49 +01:00
LUMIERA_CONDITION_SECTION ( threads , & t - > signal )
2009-12-23 19:10:31 +01:00
{
2010-01-18 00:34:53 +01:00
t - > rh = & lumiera_lock_section_ . rh ;
2009-12-23 19:10:31 +01:00
do {
// NULL function means: no work to do
2010-01-16 19:53:42 +01:00
INFO ( threads , " function %p " , t - > function ) ;
2009-12-23 19:10:31 +01:00
if ( t - > function )
t - > function ( t - > arguments ) ;
2009-12-24 01:54:49 +01:00
lumiera_threadpool_release_thread ( t ) ;
2010-01-16 19:53:42 +01:00
LUMIERA_CONDITION_WAIT ( t - > state ! = LUMIERA_THREADSTATE_IDLE ) ;
INFO ( threads , " Thread awaken with state %d " , t - > state ) ;
2009-12-23 19:10:31 +01:00
} while ( t - > state ! = LUMIERA_THREADSTATE_SHUTDOWN ) ;
2010-01-18 00:34:53 +01:00
// SHUTDOWN state
if ( t - > kind & LUMIERA_THREAD_JOINABLE )
{
INFO ( threads , " Thread zombified " ) ;
/* move error state to data the other thread will it pick up from there */
t - > arguments = ( void * ) lumiera_error ( ) ;
t - > state = LUMIERA_THREADSTATE_ZOMBIE ;
LUMIERA_CONDITION_WAIT ( t - > state = = LUMIERA_THREADSTATE_JOINED ) ;
INFO ( threads , " Thread joined " ) ;
}
2009-12-23 19:10:31 +01:00
2010-01-16 19:53:42 +01:00
INFO ( threads , " Thread Shutdown " ) ;
2009-12-23 19:10:31 +01:00
}
2010-01-18 00:34:53 +01:00
TODO ( " no error must be pending here, else do app shutdown " ) ;
2009-11-25 17:06:53 +01:00
return 0 ;
2009-11-24 01:47:20 +01:00
}
2009-11-23 01:55:08 +01:00
// when this is called it should have already been decided that the function
// shall run in parallel, as a thread
LumieraThread
lumiera_thread_run ( enum lumiera_thread_class kind ,
void ( * function ) ( void * ) ,
void * arg ,
const char * purpose ,
struct nobug_flag * flag )
{
2010-01-16 16:39:45 +01:00
TRACE ( threads ) ;
2009-12-24 01:50:59 +01:00
// REQUIRE (function, "invalid function");
2009-12-23 19:10:31 +01:00
2009-11-23 01:55:08 +01:00
// ask the threadpool for a thread (it might create a new one)
2009-12-31 13:27:45 +01:00
LumieraThread self = lumiera_threadpool_acquire_thread ( kind , purpose , flag ) ;
2009-11-23 01:55:08 +01:00
2009-12-23 19:10:31 +01:00
// set the function and data to be run
self - > function = function ;
self - > arguments = arg ;
2009-11-23 01:55:08 +01:00
2010-01-18 16:24:45 +01:00
self - > deadline . tv_sec = 0 ;
2009-11-23 22:40:31 +01:00
// and let it really run (signal the condition var, the thread waits on it)
2009-12-23 19:10:31 +01:00
self - > state = LUMIERA_THREADSTATE_WAKEUP ;
2009-12-24 01:54:49 +01:00
2010-01-08 00:36:51 +01:00
LUMIERA_CONDITION_SECTION ( cond_sync , & self - > signal )
2009-12-24 01:54:49 +01:00
LUMIERA_CONDITION_SIGNAL ;
2009-11-23 01:55:08 +01:00
// NOTE: example only, add solid error handling!
return self ;
}
2009-11-23 22:40:31 +01:00
/**
* Create a new thread structure with a matching pthread
*/
LumieraThread
lumiera_thread_new ( enum lumiera_thread_class kind ,
const char * purpose ,
2009-12-03 04:21:49 +01:00
struct nobug_flag * flag ,
pthread_attr_t * attrs )
2009-11-23 22:40:31 +01:00
{
2010-01-18 00:34:53 +01:00
pthread_once ( & lumiera_thread_initialized , lumiera_thread_tls_init ) ;
2010-01-20 13:58:22 +01:00
// TODO: do something with this string:
2009-11-23 22:40:31 +01:00
( void ) purpose ;
2009-12-03 04:21:49 +01:00
REQUIRE ( attrs , " invalid pthread attributes structure passed " ) ;
2009-11-23 22:40:31 +01:00
LumieraThread self = lumiera_malloc ( sizeof ( * self ) ) ;
2009-12-31 13:27:45 +01:00
llist_init ( & self - > node ) ;
2009-12-24 01:54:49 +01:00
lumiera_condition_init ( & self - > signal , " thread-control " , flag ) ;
2009-11-23 22:51:18 +01:00
self - > kind = kind ;
2009-12-23 19:10:31 +01:00
self - > state = LUMIERA_THREADSTATE_STARTUP ;
self - > function = NULL ;
self - > arguments = NULL ;
2010-01-18 16:24:45 +01:00
self - > deadline . tv_sec = 0 ;
self - > deadline . tv_nsec = 0 ;
2009-11-23 22:40:31 +01:00
2009-12-03 04:21:49 +01:00
int error = pthread_create ( & self - > id , attrs , & thread_loop , self ) ;
2009-11-23 22:40:31 +01:00
if ( error )
{
2009-11-26 03:46:03 +01:00
LUMIERA_DIE ( ERRNO ) ;
2009-11-23 22:40:31 +01:00
}
return self ;
}
2008-12-23 08:44:30 +01:00
2009-11-25 04:25:00 +01:00
LumieraThread
lumiera_thread_destroy ( LumieraThread self )
{
2010-01-16 16:39:45 +01:00
TRACE ( threads ) ;
2009-11-25 17:06:53 +01:00
REQUIRE ( self , " trying to destroy an invalid thread " ) ;
2009-12-31 13:27:45 +01:00
llist_unlink ( & self - > node ) ;
2009-12-23 19:10:31 +01:00
// get the pthread out of the processing loop
// need to signal to the thread that it should start quitting
// should this be within the section?
2010-01-16 19:53:42 +01:00
LUMIERA_CONDITION_SECTION ( threads , & self - > signal )
2009-12-23 19:10:31 +01:00
{
REQUIRE ( self - > state = = LUMIERA_THREADSTATE_IDLE , " trying to delete a thread in state other than IDLE (%s) " , lumiera_threadstate_names [ self - > state ] ) ;
self - > state = LUMIERA_THREADSTATE_SHUTDOWN ;
self - > function = NULL ;
self - > arguments = NULL ;
2009-12-24 01:54:49 +01:00
LUMIERA_CONDITION_SIGNAL ;
2009-12-23 19:10:31 +01:00
}
2010-01-16 19:53:42 +01:00
int error = pthread_join ( self - > id , NULL ) ;
ENSURE ( 0 = = error , " pthread_join returned %d:%s " , error , strerror ( error ) ) ;
2009-12-23 19:10:31 +01:00
// condition has to be destroyed after joining with the thread
2010-01-16 19:53:42 +01:00
lumiera_condition_destroy ( & self - > signal , & NOBUG_FLAG ( threads ) ) ;
2009-12-23 19:10:31 +01:00
2009-11-25 04:25:00 +01:00
return self ;
}
void
lumiera_thread_delete ( LumieraThread self )
{
2010-01-16 16:39:45 +01:00
TRACE ( threads ) ;
2009-11-25 04:25:00 +01:00
lumiera_free ( lumiera_thread_destroy ( self ) ) ;
}
2010-01-18 00:34:53 +01:00
LumieraThread
lumiera_thread_self ( void )
{
pthread_once ( & lumiera_thread_initialized , lumiera_thread_tls_init ) ;
return pthread_getspecific ( lumiera_thread_tls ) ;
}
2010-01-18 16:24:45 +01:00
/**
* Set a threads deadline
* A thread must finish before its deadline is hit . Otherwise it counts as stalled
* which is a fatal error which might pull the application down .
*/
LumieraThread
lumiera_thread_deadline_set ( struct timespec deadline )
{
LumieraThread self = lumiera_thread_self ( ) ;
if ( self )
self - > deadline = deadline ;
return self ;
}
/**
* Extend a threads deadline
* sets the deadline to now + ms in future . This can be used to implement a heartbeat .
*/
LumieraThread
lumiera_thread_deadline_extend ( unsigned ms )
{
LumieraThread self = lumiera_thread_self ( ) ;
if ( self )
{
struct timespec deadline ;
clock_gettime ( CLOCK_REALTIME , & deadline ) ;
deadline . tv_sec + = ms / 1000 ;
deadline . tv_nsec + = 1000000 * ( ms % 1000 ) ;
2010-01-18 18:45:02 +01:00
if ( deadline . tv_nsec > = 1000000000 )
2010-01-18 16:24:45 +01:00
{
deadline . tv_sec + = ( deadline . tv_nsec / 1000000000 ) ;
deadline . tv_nsec % = 1000000000 ;
}
self - > deadline = deadline ;
}
return self ;
}
/**
* Clear a threads deadline
* Threads without deadline will not be checked against deadlocks ( this is the default )
*/
LumieraThread
lumiera_thread_deadline_clear ( void )
{
LumieraThread self = lumiera_thread_self ( ) ;
if ( self )
{
self - > deadline . tv_sec = 0 ;
self - > deadline . tv_nsec = 0 ;
}
return self ;
}
2010-01-18 00:34:53 +01:00
LumieraThread
2010-01-18 19:58:31 +01:00
lumiera_thread_sync_other ( LumieraThread other )
2010-01-18 00:34:53 +01:00
{
TRACE ( threads ) ;
LUMIERA_CONDITION_SECTION ( threads , & other - > signal )
{
2010-01-20 23:09:37 +01:00
REQUIRE ( other - > state = = LUMIERA_THREADSTATE_SYNCING , " the other thread is in the wrong state: %s " , lumiera_threadstate_names [ other - > state ] ) ; TODO ( " Runtime error when state expectation isn't met " ) ;
2010-01-18 19:58:31 +01:00
other - > state = LUMIERA_THREADSTATE_RUNNING ;
2010-01-18 00:34:53 +01:00
LUMIERA_CONDITION_SIGNAL ;
}
return other ;
}
LumieraThread
2010-01-18 19:58:31 +01:00
lumiera_thread_sync ( void )
2010-01-18 00:34:53 +01:00
{
TRACE ( threads ) ;
LumieraThread self = lumiera_thread_self ( ) ;
REQUIRE ( self , " not a lumiera thread " ) ;
2010-01-18 19:58:31 +01:00
self - > state = LUMIERA_THREADSTATE_SYNCING ;
2010-01-18 00:34:53 +01:00
TODO ( " error handing, maybe timed mutex (using the threads heartbeat timeout, shortly before timeout) " ) ;
2010-01-18 19:58:31 +01:00
while ( self - > state = = LUMIERA_THREADSTATE_SYNCING ) {
2010-01-18 00:34:53 +01:00
lumiera_condition_wait ( & self - > signal , & NOBUG_FLAG ( threads ) , self - > rh ) ;
2010-01-18 16:23:28 +01:00
}
2010-01-18 00:34:53 +01:00
return self ;
}
lumiera_err
lumiera_thread_join ( LumieraThread thread )
{
TRACE ( threads ) ;
lumiera_err ret = NULL ;
LUMIERA_CONDITION_SECTION ( threads , & thread - > signal )
{
LUMIERA_CONDITION_WAIT ( thread - > state = = LUMIERA_THREADSTATE_ZOMBIE ) ;
ret = ( lumiera_err ) thread - > arguments ;
thread - > state = LUMIERA_THREADSTATE_JOINED ;
LUMIERA_CONDITION_SIGNAL ; /* kiss it a last goodbye */
}
return ret ;
}
2008-12-23 08:44:30 +01:00
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/