diff options
| -rw-r--r-- | ChangeLog | 15 | ||||
| -rw-r--r-- | condvar.c | 47 | ||||
| -rw-r--r-- | implement.h | 2 | ||||
| -rw-r--r-- | mutex.c | 44 | ||||
| -rw-r--r-- | rwlock.c | 1007 | 
5 files changed, 654 insertions, 461 deletions
| @@ -1,3 +1,18 @@ +Thu Sep 16 1999  Ross Johnson  <rpj@swan.canberra.edu.au> + +	* rwlock.c (pthread_rwlock_destroy): Add serialisation. +	(_rwlock_check_need_init): Check for detroyed rwlock. +	* rwlock.c: Check return codes from _rwlock_check_need_init(); +	modify comments; serialise access to rwlock objects during +	operations; rename rw_mutex to rw_lock. +	* implement.h: Rename rw_mutex to rw_lock. +	* mutex.c (pthread_mutex_destroy): Add serialisation. +	(_mutex_check_need_init): Check for detroyed mutex. +	* condvar.c (pthread_cond_destroy): Add serialisation. +	(_cond_check_need_init): Check for detroyed condvar. +	* mutex.c: Modify comments. +	* condvar.c: Modify comments. +  Sat Sep 10 12:56:13 1999  Ross Johnson  <rpj@swan.canberra.edu.au>  	The following code for POSIX read/write locks was contributed @@ -65,6 +65,15 @@ _cond_check_need_init(pthread_cond_t *cond)      {        result = pthread_cond_init(cond, NULL);      } +  else if (*cond == NULL) +    { +      /* +       * The cv has been destroyed while we were waiting to +       * initialise it, so the operation that caused the +       * auto-initialisation should fail. +       */ +      result = EINVAL; +    }    LeaveCriticalSection(&_pthread_cond_test_init_lock); @@ -455,10 +464,10 @@ pthread_cond_destroy (pthread_cond_t * cond)        return EINVAL;      } -  cv = *cond; - -  if (cv != (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) +  if (*cond != (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT)      { +      cv = *cond; +        if (pthread_mutex_lock(&(cv->waitersLock)) != 0)  	{  	  return EINVAL; @@ -476,9 +485,39 @@ pthread_cond_destroy (pthread_cond_t * cond)        (void) pthread_mutex_destroy (&(cv->waitersLock));        free(cv); +      *cond = NULL;      } +  else +    { +      /* +       * See notes in _cond_check_need_init() above also. +       */ +      EnterCriticalSection(&_pthread_cond_test_init_lock); -  *cond = NULL; +      /* +       * Check again. +       */ +      if (*cond == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) +        { +          /* +           * This is all we need to do to destroy a statically +           * initialised cond that has not yet been used (initialised). +           * If we get to here, another thread +           * waiting to initialise this cond will get an EINVAL. +           */ +          *cond = NULL; +        } +      else +        { +          /* +           * The cv has been initialised while we were waiting +           * so assume it's in use. +           */ +          result = EBUSY; +        } + +      LeaveCriticalSection(&_pthread_cond_test_init_lock); +    }    return (result);  } diff --git a/implement.h b/implement.h index b48b2f2..70836dc 100644 --- a/implement.h +++ b/implement.h @@ -164,7 +164,7 @@ struct pthread_condattr_t_ {  #define RW_MAGIC    0x19283746  struct pthread_rwlock_t_ { -    pthread_mutex_t rw_mutex;        /* basic lock on this struct */ +    pthread_mutex_t rw_lock;         /* basic lock on this struct */      pthread_cond_t  rw_condreaders;  /* for reader threads waiting */      pthread_cond_t  rw_condwriters;  /* for writer threads waiting */      int             rw_magic;        /* for error checking */ @@ -69,6 +69,15 @@ _mutex_check_need_init(pthread_mutex_t *mutex)      {        result = pthread_mutex_init(mutex, NULL);      } +  else if (*mutex == NULL) +    { +      /* +       * The mutex has been destroyed while we were waiting to +       * initialise it, so the operation that caused the +       * auto-initialisation should fail. +       */ +      result = EINVAL; +    }    LeaveCriticalSection(&_pthread_mutex_test_init_lock); @@ -177,13 +186,13 @@ pthread_mutex_destroy(pthread_mutex_t *mutex)        return EINVAL;      } -  mx = *mutex; -    /*     * Check to see if we have something to delete.     */ -  if (mx != (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) +  if (*mutex != (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT)      { +      mx = *mutex; +        if (mx->mutex == 0)  	{  	  DeleteCriticalSection(&mx->cs); @@ -203,10 +212,33 @@ pthread_mutex_destroy(pthread_mutex_t *mutex)    else      {        /* -       * This is all we need to do to destroy a statically -       * initialised mutex that has not yet been used (initialised). +       * See notes in _mutex_check_need_init() above also.         */ -      *mutex = NULL; +      EnterCriticalSection(&_pthread_mutex_test_init_lock); + +      /* +       * Check again. +       */ +      if (*mutex == (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) +        { +          /* +           * This is all we need to do to destroy a statically +           * initialised mutex that has not yet been used (initialised). +           * If we get to here, another thread +           * waiting to initialise this mutex will get an EINVAL. +           */ +          *mutex = NULL; +        } +      else +        { +          /* +           * The mutex has been initialised while we were waiting +           * so assume it's in use. +           */ +          result = EBUSY; +        } + +      LeaveCriticalSection(&_pthread_mutex_test_init_lock);      }    return(result); @@ -1,450 +1,557 @@ -/*
 - * rwlock.c
 - *
 - * Description:
 - * This translation unit implements read/write lock primitives.
 - *
 - * Pthreads-win32 - POSIX Threads Library for Win32
 - * Copyright (C) 1998
 - *
 - * This library is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU Library General Public
 - * License as published by the Free Software Foundation; either
 - * version 2 of the License, or (at your option) any later version.
 - *
 - * This library 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
 - * Library General Public License for more details.
 - *
 - * You should have received a copy of the GNU Library General Public
 - * License along with this library; if not, write to the Free
 - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 - * MA 02111-1307, USA
 - */
 -
 -#include <errno.h>
 -
 -#include "pthread.h"
 -#include "implement.h"
 -
 -static int
 -_rwlock_check_need_init(pthread_rwlock_t *rwlock)
 -{
 -  int result = 0;
 -
 -  /*
 -   * The following guarded test is specifically for statically
 -   * initialised rwlocks (via PTHREAD_RWLOCK_INITIALIZER).
 -   *
 -   * Note that by not providing this synchronisation we risk
 -   * introducing race conditions into applications which are
 -   * correctly written.
 -   *
 -   * Approach
 -   * --------
 -   * We know that static rwlocks will not be PROCESS_SHARED
 -   * so we can serialise access to internal state using
 -   * Win32 Critical Sections rather than Win32 Mutexes.
 -   *
 -   * If using a single global lock slows applications down too much,
 -   * multiple global locks could be created and hashed on some random
 -   * value associated with each mutex, the pointer perhaps. At a guess,
 -   * a good value for the optimal number of global locks might be
 -   * the number of processors + 1.
 -   *
 -   */
 -  EnterCriticalSection(&_pthread_rwlock_test_init_lock);
 -
 -  /*
 -   * We got here possibly under race
 -   * conditions. Check again inside the critical section
 -   * and only initialise if the mutex is valid (not been destroyed).
 -   * If a static mutex has been destroyed, the application can
 -   * re-initialise it only by calling pthread_mutex_init()
 -   * explicitly.
 -   */
 -  if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -    {
 -      result = pthread_rwlock_init(rwlock, NULL);
 -    }
 -
 -  LeaveCriticalSection(&_pthread_rwlock_test_init_lock);
 -
 -  return(result);
 -}
 -
 -int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
 -{
 -    int result = 0;
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL)
 -      {
 -        return EINVAL;
 -      }
 -
 -    rw = *rwlock;
 -
 -    rw = (pthread_rwlock_t) calloc(1, sizeof(*rw));
 -
 -    if (rw == NULL)
 -      {
 -        result = ENOMEM;
 -        goto fail0;
 -      }
 -
 -    if (attr != NULL
 -        && *attr != NULL)
 -      {
 -        result = EINVAL; /* Not supported */
 -        goto fail0;
 -      }
 -
 -    if ((result = pthread_mutex_init(&rw->rw_mutex, NULL)) != 0)
 -      {
 -        goto fail1;
 -      }
 -
 -    if ((result = pthread_cond_init(&rw->rw_condreaders, NULL)) != 0)
 -      {
 -        goto fail2;
 -      }
 -
 -    if ((result = pthread_cond_init(&rw->rw_condwriters, NULL)) != 0)
 -      {
 -        goto fail3;
 -      }
 -
 -    rw->rw_nwaitreaders = 0;
 -    rw->rw_nwaitwriters = 0;
 -    rw->rw_refcount = 0;
 -    rw->rw_magic = RW_MAGIC;
 -
 -    result = 0;
 -    goto fail0;
 -
 -fail3:
 -    pthread_cond_destroy(&rw->rw_condreaders);
 -
 -fail2:
 -    pthread_mutex_destroy(&rw->rw_mutex);
 -
 -fail1:
 -fail0:
 -    *rwlock = rw;
 -
 -    return(result); /* an errno value */
 -}
 -
 -int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
 -{
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL || *rwlock == NULL || (*rwlock)->rw_magic != RW_MAGIC)
 -      {
 -        return(EINVAL);
 -      }
 -
 -    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -      {
 -        /*
 -         * Destroy a static declared R/W lock that has never been
 -         * initialised.
 -         */
 -        *rwlock = NULL;
 -        return(0);
 -      }
 -
 -    rw = *rwlock;
 -
 -    if (rw->rw_refcount != 0
 -        || rw->rw_nwaitreaders != 0
 -        || rw->rw_nwaitwriters != 0)
 -      {
 -        return(EBUSY);
 -      }
 -
 -    pthread_mutex_destroy(&rw->rw_mutex);
 -    pthread_cond_destroy(&rw->rw_condreaders);
 -    pthread_cond_destroy(&rw->rw_condwriters);
 -    rw->rw_magic = 0;
 -    free(rw);
 -    *rwlock = NULL;
 -
 -    return(0);
 -}
 -
 -static void _rwlock_cancelrdwait(void *arg)
 -{
 -    pthread_rwlock_t rw;
 -
 -    rw = arg;
 -    rw->rw_nwaitreaders--;
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -}
 -
 -int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
 -{
 -    int result = 0;
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL || *rwlock == NULL)
 -      {
 -        return EINVAL;
 -      }
 -
 -    /*
 -     * We do a quick check to see if we need to do more work
 -     * to initialise a static rwlock. We check
 -     * again inside the guarded section of _rwlock_check_need_init()
 -     * to avoid race conditions.
 -     */
 -    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -      {
 -        result = _rwlock_check_need_init(rwlock);
 -      }
 -
 -    rw = *rwlock;
 -
 -    if (rw->rw_magic != RW_MAGIC)
 -      {
 -        return(EINVAL);
 -      }
 -
 -    if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
 -      {
 -        return(result);
 -      }
 -
 -    /* give preference to waiting writers */
 -    while (rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0)
 -      {
 -        rw->rw_nwaitreaders++;
 -        pthread_cleanup_push(_rwlock_cancelrdwait, rw);
 -        result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
 -        pthread_cleanup_pop(0);
 -        rw->rw_nwaitreaders--;
 -
 -        if (result != 0)
 -          {
 -            break;
 -          }
 -      }
 -
 -    if (result == 0)
 -      {
 -        rw->rw_refcount++; /* another reader has a read lock */
 -      }
 -
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -
 -    return(result);
 -}
 -
 -static void _rwlock_cancelwrwait(void *arg)
 -{
 -    pthread_rwlock_t rw;
 -
 -    rw = arg;
 -    rw->rw_nwaitwriters--;
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -}
 -
 -int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
 -{
 -    int result;
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL || *rwlock == NULL)
 -      {
 -        return EINVAL;
 -      }
 -
 -    /*
 -     * We do a quick check to see if we need to do more work
 -     * to initialise a static rwlock. We check
 -     * again inside the guarded section of _rwlock_check_need_init()
 -     * to avoid race conditions.
 -     */
 -    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -      {
 -        result = _rwlock_check_need_init(rwlock);
 -      }
 -
 -    rw = *rwlock;
 -
 -    if (rw->rw_magic != RW_MAGIC)
 -        return(EINVAL);
 -
 -    if ( (result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
 -        return(result);
 -
 -    while (rw->rw_refcount != 0) {
 -        rw->rw_nwaitwriters++;
 -        pthread_cleanup_push(_rwlock_cancelwrwait, rw);
 -        result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
 -        pthread_cleanup_pop(0);
 -        rw->rw_nwaitwriters--;
 -        if (result != 0)
 -            break;
 -    }
 -    if (result == 0)
 -        rw->rw_refcount = -1;
 -
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -    return(result);
 -}
 -
 -int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
 -{
 -    int result = 0;
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL || *rwlock == NULL)
 -      {
 -        return(EINVAL);
 -      }
 -
 -    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -      {
 -        return(0);
 -      }
 -
 -    rw = *rwlock;
 -
 -    if (rw->rw_magic != RW_MAGIC)
 -      {
 -        return(EINVAL);
 -      }
 -
 -    if ( (result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
 -      {
 -        return(result);
 -      }
 -
 -    if (rw->rw_refcount > 0)
 -      {
 -        rw->rw_refcount--;             /* releasing a reader */
 -      }
 -    else if (rw->rw_refcount == -1)
 -      {
 -        rw->rw_refcount = 0;           /* releasing a writer */
 -      }
 -    else 
 -      {
 -        return(EINVAL);
 -      }
 -
 -    result = 0;
 -
 -    /*
 -     * Give preference to waiting writers over waiting readers
 -     */
 -    if (rw->rw_nwaitwriters > 0)
 -      {
 -        if (rw->rw_refcount == 0)
 -          {
 -            result = pthread_cond_signal(&rw->rw_condwriters);
 -          }
 -      }
 -    else if (rw->rw_nwaitreaders > 0)
 -      {
 -         result = pthread_cond_broadcast(&rw->rw_condreaders);
 -      }
 -
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -    return(result);
 -}
 - 
 -int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
 -{
 -    int result = 0;
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL || *rwlock == NULL)
 -      {
 -        return EINVAL;
 -      }
 -
 -    /*
 -     * We do a quick check to see if we need to do more work
 -     * to initialise a static rwlock. We check
 -     * again inside the guarded section of _rwlock_check_need_init()
 -     * to avoid race conditions.
 -     */
 -    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -      {
 -        result = _rwlock_check_need_init(rwlock);
 -      }
 -
 -    rw = *rwlock;
 -
 -    if (rw->rw_magic != RW_MAGIC)
 -      {
 -        return(EINVAL);
 -      }
 -
 -    if ( (result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
 -      {
 -        return(result);
 -      }
 -
 -    if (rw->rw_refcount == -1 || rw->rw_nwaitwriters > 0)
 -      {
 -        result = EBUSY;    /* held by a writer or waiting writers */
 -      }
 -    else
 -      {
 -        rw->rw_refcount++; /* increment count of reader locks */
 -      }
 -
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -    return(result);
 -}
 -
 -int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
 -{
 -    int result = 0;
 -    pthread_rwlock_t rw;
 -
 -    if (rwlock == NULL || *rwlock == NULL)
 -      {
 -        return EINVAL;
 -      }
 -
 -    /*
 -     * We do a quick check to see if we need to do more work
 -     * to initialise a static rwlock. We check
 -     * again inside the guarded section of _rwlock_check_need_init()
 -     * to avoid race conditions.
 -     */
 -    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT)
 -      {
 -        result = _rwlock_check_need_init(rwlock);
 -      }
 -
 -    rw = *rwlock;
 -
 -    if (rw->rw_magic != RW_MAGIC)
 -      {
 -        return(EINVAL);
 -      }
 -
 -    if ( (result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
 -      {
 -        return(result);
 -      }
 -
 -    if (rw->rw_refcount != 0)
 -      {
 -        result = EBUSY;       /* held by either writer or reader(s) */
 -      }
 -    else
 -      {
 -        rw->rw_refcount = -1; /* available, indicate a writer has it */
 -      }
 -
 -    pthread_mutex_unlock(&rw->rw_mutex);
 -    return(result);
 -}
 +/* + * rwlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright (C) 1998 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + */ + +#include <errno.h> + +#include "pthread.h" +#include "implement.h" + +static int +_rwlock_check_need_init(pthread_rwlock_t *rwlock) +{ +  int result = 0; + +  /* +   * The following guarded test is specifically for statically +   * initialised rwlocks (via PTHREAD_RWLOCK_INITIALIZER). +   * +   * Note that by not providing this synchronisation we risk +   * introducing race conditions into applications which are +   * correctly written. +   * +   * Approach +   * -------- +   * We know that static rwlocks will not be PROCESS_SHARED +   * so we can serialise access to internal state using +   * Win32 Critical Sections rather than Win32 Mutexes. +   * +   * If using a single global lock slows applications down too much, +   * multiple global locks could be created and hashed on some random +   * value associated with each mutex, the pointer perhaps. At a guess, +   * a good value for the optimal number of global locks might be +   * the number of processors + 1. +   * +   */ +  EnterCriticalSection(&_pthread_rwlock_test_init_lock); + +  /* +   * We got here possibly under race +   * conditions. Check again inside the critical section +   * and only initialise if the rwlock is valid (not been destroyed). +   * If a static rwlock has been destroyed, the application can +   * re-initialise it only by calling pthread_rwlock_init() +   * explicitly. +   */ +  if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +    { +      result = pthread_rwlock_init(rwlock, NULL); +    } +  else if (*rwlock == NULL) +    { +      /* +       * The rwlock has been destroyed while we were waiting to +       * initialise it, so the operation that caused the +       * auto-initialisation should fail. +       */ +      result = EINVAL; +    } + +  LeaveCriticalSection(&_pthread_rwlock_test_init_lock); + +  return(result); +} + +int +pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +{ +    int result = 0; +    pthread_rwlock_t rw; + +    if (rwlock == NULL) +      { +        return EINVAL; +      } + +    rw = *rwlock; + +    rw = (pthread_rwlock_t) calloc(1, sizeof(*rw)); + +    if (rw == NULL) +      { +        result = ENOMEM; +        goto FAIL0; +      } + +    if (attr != NULL +        && *attr != NULL) +      { +        result = EINVAL; /* Not supported */ +        goto FAIL0; +      } + +    if ((result = pthread_mutex_init(&(rw->rw_lock), NULL)) != 0) +      { +        goto FAIL1; +      } + +    if ((result = pthread_cond_init(&(rw->rw_condreaders), NULL)) != 0) +      { +        goto FAIL2; +      } + +    if ((result = pthread_cond_init(&(rw->rw_condwriters), NULL)) != 0) +      { +        goto FAIL3; +      } + +    rw->rw_nwaitreaders = 0; +    rw->rw_nwaitwriters = 0; +    rw->rw_refcount = 0; +    rw->rw_magic = RW_MAGIC; + +    result = 0; +    goto FAIL0; + +FAIL3: +    (void) pthread_cond_destroy(&(rw->rw_condreaders)); + +FAIL2: +    (void) pthread_mutex_destroy(&(rw->rw_lock)); + +FAIL1: +FAIL0: +    *rwlock = rw; + +    return(result); +} + +int +pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ +    pthread_rwlock_t rw; +    int result = 0; + +    if (rwlock == NULL || *rwlock == NULL) +      { +        return(EINVAL); +      } + +    if (*rwlock != (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +      { +        rw = *rwlock; + +        if (pthread_mutex_lock(&(rw->rw_lock)) != 0) +          { +            return(EINVAL); +          } + +        if (rw->rw_magic != RW_MAGIC) +          { +            (void) pthread_mutex_unlock(&(rw->rw_lock)); +            return(EINVAL); +          } + +        if (rw->rw_refcount != 0 +            || rw->rw_nwaitreaders != 0 +            || rw->rw_nwaitwriters != 0) +          { +            (void) pthread_mutex_unlock(&(rw->rw_lock)); +            result = EBUSY; +          } +        else +          { +            rw->rw_magic = NULL; +            (void) pthread_mutex_unlock(&(rw->rw_lock)); +            (void) pthread_cond_destroy(&(rw->rw_condreaders)); +            (void) pthread_cond_destroy(&(rw->rw_condwriters)); +            (void) pthread_mutex_destroy(&(rw->rw_lock)); +  +            free(rw); +            *rwlock = NULL; +           } +      } +    else +      { +        /* +         * See notes in _rwlock_check_need_init() above also. +         */ +        EnterCriticalSection(&_pthread_rwlock_test_init_lock); + +        /* +         * Check again. +         */ +        if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +          { +            /* +             * This is all we need to do to destroy a statically +             * initialised rwlock that has not yet been used (initialised). +             * If we get to here, another thread +             * waiting to initialise this rwlock will get an EINVAL. +             */ +            *rwlock = NULL; +          } +        else +          { +            /* +             * The rwlock has been initialised while we were waiting +             * so assume it's in use. +             */ +            result = EBUSY; +          } + +        LeaveCriticalSection(&_pthread_rwlock_test_init_lock); +      } + +    return(result); +} + +static void +_rwlock_cancelrdwait(void * arg) +{ +    pthread_rwlock_t rw; + +    rw = (pthread_rwlock_t) arg; +    rw->rw_nwaitreaders--; +    pthread_mutex_unlock(&(rw->rw_lock)); +} + +int +pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ +    int result = 0; +    pthread_rwlock_t rw; + +    if (rwlock == NULL || *rwlock == NULL) +      { +        return(EINVAL); +      } + +    /* +     * We do a quick check to see if we need to do more work +     * to initialise a static rwlock. We check +     * again inside the guarded section of _rwlock_check_need_init() +     * to avoid race conditions. +     */ +    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +      { +        result = _rwlock_check_need_init(rwlock); + +        if (result != 0 && result != EBUSY) +          { +            return(result); +          } +      } + +    rw = *rwlock; + +    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +      { +        return(result); +      } + +    if (rw->rw_magic != RW_MAGIC) +      { +        result = EINVAL; +        goto FAIL1; +      } + +    /* +     * Give preference to waiting writers +     */ +    while (rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0) +      { +        rw->rw_nwaitreaders++; +        pthread_cleanup_push(_rwlock_cancelrdwait, rw); +        result = pthread_cond_wait(&(rw->rw_condreaders), &(rw->rw_lock)); +        pthread_cleanup_pop(0); +        rw->rw_nwaitreaders--; + +        if (result != 0) +          { +            break; +          } +      } + +    if (result == 0) +      { +        rw->rw_refcount++; /* another reader has a read lock */ +      } + +FAIL1: +    (void) pthread_mutex_unlock(&(rw->rw_lock)); + +    return(result); +} + +static void +_rwlock_cancelwrwait(void * arg) +{ +    pthread_rwlock_t rw; + +    rw = (pthread_rwlock_t) arg; +    rw->rw_nwaitwriters--; +    (void) pthread_mutex_unlock(&(rw->rw_lock)); +} + +int +pthread_rwlock_wrlock(pthread_rwlock_t * rwlock) +{ +    int result; +    pthread_rwlock_t rw; + +    if (rwlock == NULL || *rwlock == NULL) +      { +        return(EINVAL); +      } + +    /* +     * We do a quick check to see if we need to do more work +     * to initialise a static rwlock. We check +     * again inside the guarded section of _rwlock_check_need_init() +     * to avoid race conditions. +     */ +    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +      { +        result = _rwlock_check_need_init(rwlock); + +        if (result != 0 && result != EBUSY) +          { +            return(result); +          } +      } + +    rw = *rwlock; + +    if (rw->rw_magic != RW_MAGIC) +      { +        return(EINVAL); +      } + +    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +      { +        return(result); +      } + +    while (rw->rw_refcount != 0) +      { +        rw->rw_nwaitwriters++; +        pthread_cleanup_push(_rwlock_cancelwrwait, rw); +        result = pthread_cond_wait(&(rw->rw_condwriters), &(rw->rw_lock)); +        pthread_cleanup_pop(0); +        rw->rw_nwaitwriters--; + +        if (result != 0) +          { +            break; +          } +      } + +    if (result == 0) +      { +        rw->rw_refcount = -1; +      } + +    (void) pthread_mutex_unlock(&(rw->rw_lock)); + +    return(result); +} + +int +pthread_rwlock_unlock(pthread_rwlock_t * rwlock) +{ +    int result = 0; +    pthread_rwlock_t rw; + +    if (rwlock == NULL || *rwlock == NULL) +      { +        return(EINVAL); +      } + +    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +      { +        /* +         * Assume any race condition here is harmless. +         */ +        return(0); +      } + +    rw = *rwlock; + +    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +      { +        return(result); +      } + +    if (rw->rw_magic != RW_MAGIC) +      { +        result = EINVAL; +        goto FAIL1; +      } + +    if (rw->rw_refcount > 0) +      { +        rw->rw_refcount--;             /* releasing a reader */ +      } +    else if (rw->rw_refcount == -1) +      { +        rw->rw_refcount = 0;           /* releasing a writer */ +      } +    else  +      { +        return(EINVAL); +      } + +    result = 0; + +    /* +     * Give preference to waiting writers over waiting readers +     */ +    if (rw->rw_nwaitwriters > 0) +      { +        if (rw->rw_refcount == 0) +          { +            result = pthread_cond_signal(&(rw->rw_condwriters)); +          } +      } +    else if (rw->rw_nwaitreaders > 0) +      { +         result = pthread_cond_broadcast(&(rw->rw_condreaders)); +      } + +FAIL1: +    (void) pthread_mutex_unlock(&(rw->rw_lock)); + +    return(result); +} +  +int +pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock) +{ +    int result = 0; +    pthread_rwlock_t rw; + +    if (rwlock == NULL || *rwlock == NULL) +      { +        return(EINVAL); +      } + +    /* +     * We do a quick check to see if we need to do more work +     * to initialise a static rwlock. We check +     * again inside the guarded section of _rwlock_check_need_init() +     * to avoid race conditions. +     */ +    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +      { +        result = _rwlock_check_need_init(rwlock); + +        if (result != 0 && result != EBUSY) +          { +            return(result); +          } +      } + +    rw = *rwlock; + +    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +      { +        return(result); +      } + +    if (rw->rw_magic != RW_MAGIC) +      { +        result = EINVAL; +        goto FAIL1; +      } + +    if (rw->rw_refcount == -1 || rw->rw_nwaitwriters > 0) +      { +        result = EBUSY;    /* held by a writer or waiting writers */ +      } +    else +      { +        rw->rw_refcount++; /* increment count of reader locks */ +      } + +FAIL1: +    (void) pthread_mutex_unlock(&(rw->rw_lock)); + +    return(result); +} + +int +pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock) +{ +    int result = 0; +    pthread_rwlock_t rw; + +    if (rwlock == NULL || *rwlock == NULL) +      { +        return(EINVAL); +      } + +    /* +     * We do a quick check to see if we need to do more work +     * to initialise a static rwlock. We check +     * again inside the guarded section of _rwlock_check_need_init() +     * to avoid race conditions. +     */ +    if (*rwlock == (pthread_rwlock_t) _PTHREAD_OBJECT_AUTO_INIT) +      { +        result = _rwlock_check_need_init(rwlock); + +        if (result != 0 && result != EBUSY) +          { +            return(result); +          } +      } + +    rw = *rwlock; + +    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +      { +        return(result); +      } + +    if (rw->rw_magic != RW_MAGIC) +      { +        result = EINVAL; +        goto FAIL1; +      } + +    if (rw->rw_refcount != 0) +      { +        result = EBUSY;       /* held by either writer or reader(s) */ +      } +    else +      { +        rw->rw_refcount = -1; /* available, indicate a writer has it */ +      } + +FAIL1: +    (void) pthread_mutex_unlock(&(rw->rw_lock)); + +    return(result); +} | 
