ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/alpha-snapshots/pthreads.0.9.2.tgz#/pthreads-0.9.2/locklib/locks_priority.c

This is locks_priority.c in view mode; [Download] [Up]

/*
 * --  FILE:           locks_priority.c
 *     AUTHOR:         Michael T. Peterson
 *
 *     Copyright (C) 1994, by Michael T. Peterson
 *     All rights reserved.
 *
 *     Permission is granted for noncommercial, private use of this software.
 *     You are expressely prohibited from selling this software, distributing
 *     this software with (or within) another product, or removing this notice.
 */
#include <utils.h>
#include <locks.h>

#define UNLOCK(l)  ((void)pthread_mutex_unlock( &(l) ))
#define LOCK(l)    ((void)pthread_mutex_lock( &(l) ))
#define RETURN(s) \
{\
   (void) pthread_mutex_unlock( &liblock ); \
   return(s); \
}

static pthread_once_t once_block = pthread_once_init;
static int lib_initialized = FALSE;
static pthread_mutex_t liblock;

static void _prio_init( void )
{
   pthread_mutex_init( &liblock, NULL );
   lib_initialized = TRUE;
}

struct PRIO_LOCK
{
   pthread_mutex_t guard_mutex;
   pthread_mutex_t prio_mutex;
   pthread_cond_t  prio_cond;
   pthread_t       owner_th;

   int             locked;
   long            owners_saved_prio;
   long            prio_ceiling;       /* Highest priority blocked thread */
   long            prio_cond_waiters;
};

int
pthread_priolock_lock_np( const pthread_priolock_t *prio_lock )
{
   struct PRIO_LOCK *plock;
   int current_prio;
   pthread_t self;   

   pthread_once( &once_block, _prio_init );
   if( prio_lock == NULL )
       return( ENOLCK );

   if((plock = *prio_lock) == NULL )
       return( ENOLCK );

   self = pthread_self();
   pthread_getprio_np( self, &current_prio );
       
   /*
    *  --  If the current ceiling is lower than the calling thread's
    *      priority, raise the ceiling.
    */
   pthread_mutex_lock( &plock->guard_mutex );

   if( current_prio > plock->prio_ceiling )
   {
       plock->prio_ceiling = current_prio;

       /*
        *  --  If the mutex is locked, then raise the priority of the
        *      the current owner.
        */
       if( plock->locked )
           pthread_setprio_np( plock->owner_th, current_prio );
   }

   pthread_mutex_unlock( &plock->guard_mutex );


   /*
    * Bump the waiter count in the event we are caused to wait while
    * another thread is adjusting the priority ceiling of this lock.
    * If we don't have to wait, i.e., the lock is currently unlocked,
    * no harm done.  We just keep on cruisin...
    */
   pthread_mutex_lock( &plock->prio_mutex );
   plock->prio_cond_waiters += 1;

   while( plock->locked )
       pthread_cond_wait( &plock->prio_cond, &plock->prio_mutex );

   plock->prio_cond_waiters -= 1;
   plock->owner_th = self;
   plock->locked = TRUE;
       
   if( plock->prio_ceiling > current_prio )
       plock->owners_saved_prio = current_prio;
           
   pthread_mutex_unlock( &plock->prio_mutex );

   return( SUCCESS );
}
          
int
pthread_priolock_unlock_np( const pthread_priolock_t *prio_lock )
{
   struct PRIO_LOCK *plock;

   pthread_once( &once_block, _prio_init );

   if( prio_lock == NULL )
       return( ENOLCK );
   
   if( (plock = *prio_lock) == NULL)
       return( ENOLCK );

   pthread_mutex_lock( &plock->prio_mutex );

   plock->locked = FALSE;

   if( plock->prio_cond_waiters )
       pthread_cond_signal( &plock->prio_cond );   
   else
       plock->prio_ceiling = plock->owners_saved_prio;

   pthread_setprio_np( pthread_self(), plock->owners_saved_prio );
   pthread_mutex_unlock( &plock->prio_mutex );

   return( SUCCESS );
}


int
pthread_priolock_init_np( pthread_priolock_t *prio_lock )
{
   struct PRIO_LOCK *plock = NULL;
   int status;

   pthread_once( &once_block, _prio_init );

   /*
    * If any of the following initialization calls fail, deallocate and
    * free all resources that had been initialized up to that point.
    */
   plock = (struct PRIO_LOCK *) malloc_r( sizeof( struct PRIO_LOCK ));
   if( plock == NULL )
   {
       prio_lock = NULL;
       return( ENOMEM );
   }

   status = pthread_mutex_init( &plock->guard_mutex, NULL );
   if( status != SUCCESS )
   {
       free_r( plock );
       return( status );
   }

   status = pthread_mutex_init( &plock->prio_mutex, NULL );
   if( status != SUCCESS )
   {
       (void) pthread_mutex_destroy( &plock->guard_mutex );
       free_r( plock );
       return( status );
   }

   status = pthread_cond_init( &plock->prio_cond, NULL );
   if( status != SUCCESS )
   {
       (void) pthread_mutex_destroy( &plock->guard_mutex );
       (void) pthread_mutex_destroy( &plock->prio_mutex );
       free_r( plock );
       return( status );
   }

   plock->prio_ceiling = -1;
   plock->owners_saved_prio = -1;
   plock->prio_cond_waiters = 0;

   *prio_lock = (struct PRIO_LOCK *) plock;
   return( SUCCESS );
}
         
int
pthread_priolock_destroy_np( pthread_priolock_t *prio_lock )
{
   struct PRIO_LOCK *plock;
   int status = SUCCESS, not_ok = FALSE;

   pthread_once( &once_block, _prio_init );
   LOCK( liblock );

   /*
    * This next step dereferences the prio_lock handle AND checks
    * whether the handle is still valid.  This routine can be
    * entered by multiple threads and this check ensures that we
    * do not attempt to destroy a priority lock that has already
    * been destroyed, perhaps while we were waiting at the previous
    * mutex.
    */
   if( prio_lock == NULL )
       RETURN( ENOLCK );


   if( (plock = *prio_lock) == NULL )
       RETURN( ENOLCK );

   /*
    * Return an error (EBUSY) if the priority lock is currently
    * in use.
    */
   not_ok = plock->locked || plock->prio_cond_waiters;
   if( not_ok )
       RETURN( EBUSY );

   status = pthread_mutex_destroy( &plock->guard_mutex );
   if( status != SUCCESS )
       RETURN( status );

   status = pthread_mutex_destroy( &plock->prio_mutex );
   if( status != SUCCESS )
       RETURN( status );

   status = pthread_cond_destroy( &plock->prio_cond );
   if( status != SUCCESS )
       RETURN( status );

   free_r( plock );
   *prio_lock = NULL;

   RETURN( status );
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.