Skip to content
Snippets Groups Projects
Commit 3fc2d951 authored by edgrif's avatar edgrif
Browse files

add remodelled thread interface code.

parent d0b73466
No related branches found
No related tags found
No related merge requests found
/* File: zmapThreads.c
* Author: Ed Griffiths (edgrif@sanger.ac.uk)
* Copyright (c) Sanger Institute, 2005
*-------------------------------------------------------------------
* ZMap 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
*-------------------------------------------------------------------
* This file is part of the ZMap genome database package
* originated by
* Ed Griffiths (Sanger Institute, UK) edgrif@sanger.ac.uk,
* Rob Clack (Sanger Institute, UK) rnc@sanger.ac.uk
*
* Description: Code to implement communcation between a control
* thread and a slave thread. This code knows nothing
* about what it is passing, it just handles the passing
* and returning of data.
*
* Exported functions: See ZMap/zmapThread.h
* HISTORY:
* Last edited: Feb 1 16:28 2005 (edgrif)
* Created: Thu Jan 27 11:25:37 2005 (edgrif)
* CVS info: $Id: zmapThreads.c,v 1.1 2005-02-02 14:41:09 edgrif Exp $
*-------------------------------------------------------------------
*/
#include <string.h>
#include <ZMap/zmapUtils.h>
#include <zmapThreads_P.h>
/* Turn on/off all debugging messages for threads. */
gboolean zmap_thread_debug_G = FALSE ;
static ZMapThread createThread(ZMapThreadRequestHandlerFunc handler_func) ;
static void destroyThread(ZMapThread thread) ;
ZMapThread zMapThreadCreate(ZMapThreadRequestHandlerFunc handler_func)
{
ZMapThread thread ;
pthread_t thread_id ;
pthread_attr_t thread_attr ;
int status = 0 ;
zMapAssert(handler_func) ;
thread = createThread(handler_func) ;
/* ok to just set state here because we have not started the thread yet.... */
zmapCondVarCreate(&(thread->request)) ;
thread->request.state = ZMAPTHREAD_REQUEST_WAIT ;
thread->request.request = NULL ;
zmapVarCreate(&(thread->reply)) ;
#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
thread->reply.state = ZMAPTHREAD_REPLY_INIT ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */
thread->reply.state = ZMAPTHREAD_REPLY_WAIT ;
thread->reply.reply = NULL ;
thread->reply.error_msg = NULL ;
/* Set the new threads attributes so it will run "detached", we do not want anything from them.
* when they die, we want them to go away and release their resources. */
if (status == 0
&& (status = pthread_attr_init(&thread_attr)) != 0)
{
zMapLogFatalSysErr(status, "%s", "Create thread attibutes") ;
}
if (status == 0
&& (status = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED)) != 0)
{
zMapLogFatalSysErr(status, "%s", "Set thread detached attibute") ;
}
/* Create the new thread. */
if (status == 0
&& (status = pthread_create(&thread_id, &thread_attr, zmapNewThread, (void *)thread)) != 0)
{
zMapLogFatalSysErr(status, "%s", "Thread creation") ;
}
if (status == 0)
thread->thread_id = thread_id ;
else
{
/* Ok to just destroy thread here as the thread was not successfully created so
* there can be no complications with interactions with condvars in connect struct. */
destroyThread(thread) ;
thread = NULL ;
}
return thread ;
}
void zMapThreadRequest(ZMapThread thread, void *request)
{
zmapCondVarSignal(&thread->request, ZMAPTHREAD_REQUEST_EXECUTE, request) ;
return ;
}
gboolean zMapThreadGetReply(ZMapThread thread, ZMapThreadReply *state)
{
gboolean got_value ;
got_value = zmapVarGetValue(&(thread->reply), state) ;
return got_value ;
}
void zMapThreadSetReply(ZMapThread thread, ZMapThreadReply state)
{
zmapVarSetValue(&(thread->reply), state) ;
return ;
}
gboolean zMapThreadGetReplyWithData(ZMapThread thread, ZMapThreadReply *state,
void **data, char **err_msg)
{
gboolean got_value ;
got_value = zmapVarGetValueWithData(&(thread->reply), state, data, err_msg) ;
return got_value ;
}
pthread_t zMapThreadGetThreadid(ZMapThread thread)
{
return thread->thread_id ;
}
/* Must be kept in step with declaration of ZMapThreadRequest enums in zmapThread_P.h */
char *zMapThreadGetRequestString(ZMapThreadRequest signalled_state)
{
char *str_states[] = {"ZMAPTHREAD_REQUEST_INIT", "ZMAPTHREAD_REQUEST_WAIT", "ZMAPTHREAD_REQUEST_TIMED_OUT",
"ZMAPTHREAD_REQUEST_GETDATA"} ;
return str_states[signalled_state] ;
}
/* Must be kept in step with declaration of ZMapThreadReply enums in zmapThread_P.h */
char *zMapThreadGetReplyString(ZMapThreadReply signalled_state)
{
char *str_states[] = {"ZMAPTHREAD_REPLY_INIT", "ZMAPTHREAD_REPLY_WAIT",
"ZMAPTHREAD_REPLY_GOTDATA", "ZMAPTHREAD_REPLY_REQERROR",
"ZMAPTHREAD_REPLY_DIED", "ZMAPTHREAD_REPLY_CANCELLED"} ;
return str_states[signalled_state] ;
}
/* Kill the thread by cancelling it, as this will asynchronously we cannot release the threads
* resources in this call. */
void zMapThreadKill(ZMapThread thread)
{
int status ;
ZMAPTHREAD_DEBUG(("GUI: killing and destroying thread for thread %lu\n", thread->thread_id)) ;
/* we could signal an exit here by setting a condvar of EXIT...but that might lead to
* deadlocks, think about this bit.. */
/* Signal the thread to cancel it */
if ((status = pthread_cancel(thread->thread_id)) != 0)
{
zMapLogFatalSysErr(status, "%s", "Thread cancel") ;
}
return ;
}
/* Release the threads resources, don't do this until the slave thread has gone. */
void zMapThreadDestroy(ZMapThread thread)
{
ZMAPTHREAD_DEBUG(("GUI: destroying thread for thread %lu\n", thread->thread_id)) ;
zmapVarDestroy(&thread->reply) ;
zmapCondVarDestroy(&(thread->request)) ;
g_free(thread) ;
return ;
}
/*
* --------------------- Internal routines ------------------------------
*/
static ZMapThread createThread(ZMapThreadRequestHandlerFunc handler_func)
{
ZMapThread thread ;
thread = g_new0(ZMapThreadStruct, 1) ;
thread->handler_func = handler_func ;
return thread ;
}
/* some care needed in using this....what about the condvars, when can they be freed ?
* CHECK THIS ALL WORKS.... */
static void destroyThread(ZMapThread thread)
{
/* Setting this to zero prevents subtle bugs where calling code continues
* to try to reuse a defunct control block. */
/* Should crash if this returns NULL, need my macros from acedb code.... */
memset((void *)thread, 0, sizeof(ZMapThreadStruct)) ;
g_free(thread) ;
return ;
}
/* File: zmapThreadsUtils.c
* Author: Ed Griffiths (edgrif@sanger.ac.uk)
* Copyright (c) Sanger Institute, 2005
*-------------------------------------------------------------------
* ZMap 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
*-------------------------------------------------------------------
* This file is part of the ZMap genome database package
* originated by
* Ed Griffiths (Sanger Institute, UK) edgrif@sanger.ac.uk,
* Rob Clack (Sanger Institute, UK) rnc@sanger.ac.uk
*
* Description: Utility functions for the slave thread interface.
*
* Exported functions: See ZMap/zmapThreads.h
* HISTORY:
* Last edited: Feb 1 09:57 2005 (edgrif)
* Created: Thu Jan 27 11:50:01 2005 (edgrif)
* CVS info: $Id: zmapThreadsUtils.c,v 1.1 2005-02-02 14:41:09 edgrif Exp $
*-------------------------------------------------------------------
*/
#include <errno.h>
#include <ZMap/zmapUtils.h>
#include <zmapThreads_P.h>
static void releaseCondvarMutex(void *thread_data) ;
static int getAbsTime(const TIMESPEC *relative_timeout, TIMESPEC *abs_timeout) ;
void zmapCondVarCreate(ZMapRequest thread_state)
{
int status ;
if ((status = pthread_mutex_init(&(thread_state->mutex), NULL)) != 0)
{
zMapLogFatalSysErr(status, "%s", "mutex init") ;
}
if ((status = pthread_cond_init(&(thread_state->cond), NULL)) != 0)
{
zMapLogFatalSysErr(status, "%s", "cond init") ;
}
thread_state->state = ZMAPTHREAD_REQUEST_INIT ;
thread_state->request = NULL ;
return ;
}
void zmapCondVarSignal(ZMapRequest thread_state, ZMapThreadRequest new_state, void *request)
{
int status ;
pthread_cleanup_push(releaseCondvarMutex, (void *)thread_state) ;
if ((status = pthread_mutex_lock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarSignal mutex lock") ;
}
thread_state->state = new_state ;
/* For some requests there will be no data. */
if (request)
thread_state->request = request ;
if ((status = pthread_cond_signal(&(thread_state->cond))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarSignal cond signal") ;
}
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarSignal mutex unlock") ;
}
pthread_cleanup_pop(0) ; /* 0 => only call cleanup if cancelled. */
return ;
}
/* Blocking wait.... */
void zmapCondVarWait(ZMapRequest thread_state, ZMapThreadRequest waiting_state)
{
int status ;
pthread_cleanup_push(releaseCondvarMutex, (void *)thread_state) ;
if ((status = pthread_mutex_lock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarWait mutex lock") ;
}
while (thread_state->state == waiting_state)
{
if ((status = pthread_cond_wait(&(thread_state->cond), &(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarWait cond wait") ;
}
}
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarWait mutex unlock") ;
}
pthread_cleanup_pop(0) ; /* 0 => only call cleanup if cancelled. */
return ;
}
/* timed wait, returns FALSE if cond var was not signalled (i.e. it timed out),
* TRUE otherwise....
* NOTE that you can optionally get the condvar state reset to the waiting_state, this
* can be useful if you are calling this routine from a loop in which you wait until
* something has changed from the waiting state...i.e. somehow you need to return to the
* waiting state before looping again. */
ZMapThreadRequest zmapCondVarWaitTimed(ZMapRequest condvar, ZMapThreadRequest waiting_state,
TIMESPEC *relative_timeout, gboolean reset_to_waiting,
void **data_out)
{
ZMapThreadRequest signalled_state = ZMAPTHREAD_REQUEST_INIT ;
int status ;
TIMESPEC abs_timeout ;
pthread_cleanup_push(releaseCondvarMutex, (void *)condvar) ;
if ((status = pthread_mutex_lock(&(condvar->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarWait mutex lock") ;
}
/* Get the relative timeout converted to absolute for the call. */
#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
if ((status = pthread_get_expiration_np(relative_timeout, &abs_timeout)) != 0)
zMapLogFatalSysErr(status, "%s", "zmapCondVarWaitTimed invalid time") ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */
if ((status = getAbsTime(relative_timeout, &abs_timeout)) != 0)
zMapLogFatalSysErr(status, "%s", "zmapCondVarWaitTimed invalid time") ;
while (condvar->state == waiting_state)
{
if ((status = pthread_cond_timedwait(&(condvar->cond), &(condvar->mutex),
&abs_timeout)) != 0)
{
if (status == ETIMEDOUT) /* Timed out so return. */
{
condvar->state = ZMAPTHREAD_REQUEST_TIMED_OUT ;
break ;
}
else
zMapLogFatalSysErr(status, "%s", "zmapCondVarWait cond wait") ;
}
}
signalled_state = condvar->state ; /* return signalled end state. */
/* optionally reset current state to wait state. */
if (reset_to_waiting)
condvar->state = waiting_state ;
/* Return data if there is some, seems to make sense only to do this if we _haven't_ timed out.
* Note how we reset condvar->request so we detect if new data comes in next time. */
if (condvar->state != ZMAPTHREAD_REQUEST_TIMED_OUT)
{
if (condvar->request)
{
*data_out = condvar->request ;
condvar->request = NULL ;
}
}
if ((status = pthread_mutex_unlock(&(condvar->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapCondVarWait mutex unlock") ;
}
pthread_cleanup_pop(0) ; /* 0 => only call cleanup if cancelled. */
return signalled_state ;
}
void zmapCondVarDestroy(ZMapRequest thread_state)
{
int status ;
if ((status = pthread_cond_destroy(&(thread_state->cond))) != 0)
{
zMapLogFatalSysErr(status, "%s", "cond destroy") ;
}
if ((status = pthread_mutex_destroy(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "mutex destroy") ;
}
return ;
}
/* this set of routines manipulates the variable in the thread state struct but do not
* involve the Condition Variable. */
void zmapVarCreate(ZMapReply thread_state)
{
int status ;
if ((status = pthread_mutex_init(&(thread_state->mutex), NULL)) != 0)
{
zMapLogFatalSysErr(status, "%s", "mutex init") ;
}
thread_state->state = ZMAPTHREAD_REPLY_INIT ;
return ;
}
void zmapVarSetValue(ZMapReply thread_state, ZMapThreadReply new_state)
{
int status ;
if ((status = pthread_mutex_lock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarSetValue mutex lock") ;
}
thread_state->state = new_state ;
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarSetValue mutex unlock") ;
}
return ;
}
/* Returns TRUE if it could read the value (i.e. the mutex was unlocked)
* and returns the value in state_out,
* returns FALSE otherwise. */
gboolean zmapVarGetValue(ZMapReply thread_state, ZMapThreadReply *state_out)
{
gboolean unlocked = TRUE ;
int status ;
if ((status = pthread_mutex_trylock(&(thread_state->mutex))) != 0)
{
if (status == EBUSY)
unlocked = FALSE ;
else
zMapLogFatalSysErr(status, "%s", "zmapVarGetValue mutex lock") ;
}
else
{
*state_out = thread_state->state ;
unlocked = TRUE ;
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarGetValue mutex unlock") ;
}
}
return unlocked ;
}
void zmapVarSetValueWithData(ZMapReply thread_state, ZMapThreadReply new_state, void *data)
{
int status ;
if ((status = pthread_mutex_lock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarSetValueWithData mutex lock") ;
}
thread_state->state = new_state ;
thread_state->reply = data ;
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarSetValueWithData mutex unlock") ;
}
return ;
}
void zmapVarSetValueWithError(ZMapReply thread_state, ZMapThreadReply new_state, char *err_msg)
{
int status ;
if ((status = pthread_mutex_lock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarSetValueWithError mutex lock") ;
}
thread_state->state = new_state ;
if (err_msg)
thread_state->error_msg = err_msg ;
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarSetValueWithError mutex unlock") ;
}
return ;
}
/* Returns TRUE if it could read the value (i.e. the mutex was unlocked)
* and returns the value in state_out and also if there is any data it
* is returned in data_out and if there is an err_msg it is returned in err_msg_out,
* returns FALSE if it could not read the value. */
gboolean zmapVarGetValueWithData(ZMapReply thread_state, ZMapThreadReply *state_out,
void **data_out, char **err_msg_out)
{
gboolean unlocked = TRUE ;
int status ;
if ((status = pthread_mutex_trylock(&(thread_state->mutex))) != 0)
{
if (status == EBUSY)
unlocked = FALSE ;
else
zMapLogFatalSysErr(status, "%s", "zmapVarGetValue mutex lock") ;
}
else
{
*state_out = thread_state->state ;
if (thread_state->reply)
{
*data_out = thread_state->reply ;
thread_state->reply = NULL ;
}
if (thread_state->error_msg)
{
*err_msg_out = thread_state->error_msg ;
thread_state->error_msg = NULL ;
}
unlocked = TRUE ;
if ((status = pthread_mutex_unlock(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "zmapVarGetValue mutex unlock") ;
}
}
return unlocked ;
}
void zmapVarDestroy(ZMapReply thread_state)
{
int status ;
if ((status = pthread_mutex_destroy(&(thread_state->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "mutex destroy") ;
}
return ;
}
/*
* ----------------------- Internal Routines -----------------------
*/
/* Called when a thread gets cancelled while waiting on a mutex to ensure that the mutex
* gets released. */
static void releaseCondvarMutex(void *thread_data)
{
ZMapRequest condvar = (ZMapRequest)thread_data ;
int status ;
ZMAPTHREAD_DEBUG(("releaseCondvarMutex cleanup handler\n")) ;
if ((status = pthread_mutex_unlock(&(condvar->mutex))) != 0)
{
zMapLogFatalSysErr(status, "%s", "releaseCondvarMutex cleanup handler - mutex unlock") ;
}
return ;
}
/* This function is a cheat really. You can only portably get the time in seconds
* as far as I can see so specifying small relative timeouts will not work....
* to this end I have inserted code to check that the relative timeout is not
* less than a single clock tick, hardly perfect but better than nothing.
*
* On the alpha you can do this:
*
* pthread_get_expiration_np(relative_timeout, &abs_timeout)
*
* to get a timeout in seconds and nanoseconds but this call is unavailable on
* Linux at least....
*
* */
static int getAbsTime(const TIMESPEC *relative_timeout, TIMESPEC *abs_timeout)
{
int status = 1 ; /* Fail by default. */
static int clock_tick_nano ;
clock_tick_nano = 1000000000 / CLK_TCK ; /* CLK_TCK can be a function. */
if ((relative_timeout->tv_sec > 0 && relative_timeout->tv_nsec >= 0)
|| (relative_timeout->tv_sec == 0 && relative_timeout->tv_nsec > clock_tick_nano))
{
*abs_timeout = *relative_timeout ; /* struct copy */
abs_timeout->tv_sec += time(NULL) ;
status = 0 ;
}
return status ;
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment