
Now that we have 3 mutexes lying around, it's important that we check and free these if necessary if error() is called in any thread that can hold them. To do this, we now have flexthread.c, which defines a flexthread_mutex struct. This is a wrapper around a pthread_mutex_t and a pthread_t. The idea is that in the error handler, the thread can check whether it holds the mutex and can free it if and only if it does. This is important because pthread fast mutexes can be freed by *any* thread, not just the thread which holds them. Note: it is only ever safe for a thread to check if it holds the mutex itself. It is *never* safe to check if another thread holds a mutex without first locking that mutex, which makes the whole operation rather pointless.
76 lines
1.5 KiB
C
76 lines
1.5 KiB
C
#include "flexthread.h"
|
|
#include "util.h"
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
struct flexthread_mutex * flexthread_mutex_create(void)
|
|
{
|
|
struct flexthread_mutex * ftm =
|
|
xmalloc( sizeof( struct flexthread_mutex ) );
|
|
|
|
FATAL_UNLESS( 0 == pthread_mutex_init( &ftm->mutex, NULL ),
|
|
"Mutex initialisation failed" );
|
|
return ftm;
|
|
|
|
}
|
|
|
|
|
|
void flexthread_mutex_destroy( struct flexthread_mutex * ftm )
|
|
{
|
|
NULLCHECK( ftm );
|
|
|
|
if( flexthread_mutex_held( ftm ) ) {
|
|
flexthread_mutex_unlock( ftm );
|
|
}
|
|
else if ( (pthread_t)NULL != ftm->holder ) {
|
|
/* This "should never happen": if we can try to destroy
|
|
* a mutex currently held by another thread, there's a
|
|
* logic bug somewhere. I know the test here is racy,
|
|
* but there's not a lot we can do about it at this
|
|
* point.
|
|
*/
|
|
fatal( "Attempted to destroy a flexthread_mutex"\
|
|
" held by another thread!" );
|
|
}
|
|
|
|
FATAL_UNLESS( 0 == pthread_mutex_destroy( &ftm->mutex ),
|
|
"Mutex destroy failed" );
|
|
free( ftm );
|
|
}
|
|
|
|
|
|
int flexthread_mutex_lock( struct flexthread_mutex * ftm )
|
|
{
|
|
NULLCHECK( ftm );
|
|
|
|
int failure = pthread_mutex_lock( &ftm->mutex );
|
|
if ( 0 == failure ) {
|
|
ftm->holder = pthread_self();
|
|
}
|
|
|
|
return failure;
|
|
}
|
|
|
|
|
|
int flexthread_mutex_unlock( struct flexthread_mutex * ftm )
|
|
{
|
|
NULLCHECK( ftm );
|
|
|
|
pthread_t orig = ftm->holder;
|
|
ftm->holder = (pthread_t)NULL;
|
|
int failure = pthread_mutex_unlock( &ftm->mutex );
|
|
if ( 0 != failure ) {
|
|
ftm->holder = orig;
|
|
}
|
|
return failure;
|
|
}
|
|
|
|
|
|
int flexthread_mutex_held( struct flexthread_mutex * ftm )
|
|
{
|
|
NULLCHECK( ftm );
|
|
return pthread_self() == ftm->holder;
|
|
}
|
|
|