blob: c770116e3100f91279325ecb6dec9764df67906b [file] [log] [blame]
/*
* Copyright (c) 2000-2001 Silicon Graphics, Inc.
* All Rights Reserved.
*
* 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.
*
* This program is distributed in the hope that it would 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 the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
#include "config.h"
#include "types.h"
#include "qlock.h"
#include "mlog.h"
struct qlock {
ix_t ql_ord;
/* ordinal position of this lock
*/
pthread_mutex_t ql_mutex;
};
typedef struct qlock qlock_t;
/* internal qlock
*/
typedef size_t ordmap_t;
/* bitmap of ordinals. used to track what ordinals have
* been allocated.
*/
#define ORDMAX ( 8 * sizeof( ordmap_t ))
/* ordinals must fit into a wordsize bitmap
*/
static ordmap_t qlock_ordalloced;
/* to enforce allocation of only one lock to each ordinal value
*/
static __thread ordmap_t thread_ordmap;
/* holds the ordmap for each thread
*/
#define QLOCK_ORDMAP_SET( ordmap, ord ) ( ordmap |= 1U << ord )
/* sets the ordinal bit in an ordmap
*/
#define QLOCK_ORDMAP_CLR( ordmap, ord ) ( ordmap &= ~( 1U << ord ))
/* clears the ordinal bit in an ordmap
*/
#define QLOCK_ORDMAP_GET( ordmap, ord ) ( ordmap & ( 1U << ord ))
/* checks if ordinal set in ordmap
*/
#define QLOCK_ORDMAP_CHK( ordmap, ord ) ( ordmap & (( 1U << ord ) - 1U ))
/* checks if any bits less than ord are set in the ordmap
*/
qlockh_t
qlock_alloc( ix_t ord )
{
qlock_t *qlockp;
/* verify the ordinal is not already taken, and mark as taken
*/
assert( ! QLOCK_ORDMAP_GET( qlock_ordalloced, ord ));
QLOCK_ORDMAP_SET( qlock_ordalloced, ord );
/* allocate lock memory
*/
qlockp = ( qlock_t * )calloc( 1, sizeof( qlock_t ));
assert( qlockp );
/* initialize the mutex
*/
pthread_mutex_init( &qlockp->ql_mutex, NULL );
/* assign the ordinal position
*/
qlockp->ql_ord = ord;
return ( qlockh_t )qlockp;
}
void
qlock_lock( qlockh_t qlockh )
{
qlock_t *qlockp = ( qlock_t * )qlockh;
pthread_t tid;
/* REFERENCED */
int rval;
/* get the caller's tid
*/
tid = pthread_self();
/* assert that this lock not already held by this thread
*/
if ( QLOCK_ORDMAP_GET( thread_ordmap, qlockp->ql_ord )) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_NOLOCK,
_("lock already held: tid %lu ord %d map %x\n"),
tid,
qlockp->ql_ord,
thread_ordmap );
}
assert( ! QLOCK_ORDMAP_GET( thread_ordmap, qlockp->ql_ord ));
/* assert that no locks with a lesser ordinal are held by this thread
*/
if ( QLOCK_ORDMAP_CHK( thread_ordmap, qlockp->ql_ord )) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_NOLOCK,
_("lock ordinal violation: tid %lu ord %d map %x\n"),
tid,
qlockp->ql_ord,
thread_ordmap );
}
assert( ! QLOCK_ORDMAP_CHK( thread_ordmap, qlockp->ql_ord ));
/* acquire the lock
*/
rval = pthread_mutex_lock( &qlockp->ql_mutex );
assert( !rval );
/* add ordinal to this threads ordmap
*/
QLOCK_ORDMAP_SET( thread_ordmap, qlockp->ql_ord );
}
void
qlock_unlock( qlockh_t qlockh )
{
qlock_t *qlockp = ( qlock_t * )qlockh;
/* REFERENCED */
int rval;
/* verify lock is held by this thread
*/
assert( QLOCK_ORDMAP_GET( thread_ordmap, qlockp->ql_ord ));
/* clear lock's ord from thread's ord map
*/
QLOCK_ORDMAP_CLR( thread_ordmap, qlockp->ql_ord );
/* release the lock
*/
rval = pthread_mutex_unlock( &qlockp->ql_mutex );
assert( ! rval );
}
qsemh_t
qsem_alloc( ix_t cnt )
{
sem_t *semp;
int rval;
/* allocate a semaphore
*/
semp = ( sem_t * )calloc( 1, sizeof( sem_t ));
assert( semp );
/* initialize the semaphore
*/
rval = sem_init( semp, 0, cnt );
assert( !rval );
return ( qsemh_t )semp;
}
void
qsem_free( qsemh_t qsemh )
{
sem_t *semp = ( sem_t * )qsemh;
int rval;
/* destroy the mutex and condition
*/
rval = sem_destroy( semp );
assert( !rval );
/* free the semaphore
*/
free( semp );
}
void
qsemP( qsemh_t qsemh )
{
sem_t *semp = ( sem_t * )qsemh;
int rval;
/* "P" the semaphore
*/
rval = sem_wait( semp );
assert( !rval );
}
void
qsemV( qsemh_t qsemh )
{
sem_t *semp = ( sem_t * )qsemh;
int rval;
/* "V" the semaphore
*/
rval = sem_post( semp );
assert( !rval );
}
bool_t
qsemPwouldblock( qsemh_t qsemh )
{
sem_t *semp = ( sem_t * )qsemh;
int count;
int rval;
rval = sem_getvalue( semp, &count );
assert( !rval );
return count <= 0 ? BOOL_TRUE : BOOL_FALSE;
}
size_t
qsemPavail( qsemh_t qsemh )
{
sem_t *semp = ( sem_t * )qsemh;
int count;
int rval;
rval = sem_getvalue( semp, &count );
assert( !rval );
return count < 0 ? 0 : count;
}