| /* |
| * 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; |
| } |