diff options
| author | rpj <rpj> | 2001-02-07 03:48:23 +0000 | 
|---|---|---|
| committer | rpj <rpj> | 2001-02-07 03:48:23 +0000 | 
| commit | 2f4a1905d1a8c424900a8615ec730f7637482525 (patch) | |
| tree | 9c35a724f9d033b81afb26c83612a2d88208a073 | |
| parent | 9a59a1b4611cbd4b1fcf7549a97dc2cbe340b794 (diff) | |
Revamp read-write locks and update cond vars.
See ChangeLog.
| -rw-r--r-- | ChangeLog | 12 | ||||
| -rw-r--r-- | condvar.c | 371 | ||||
| -rw-r--r-- | implement.h | 159 | ||||
| -rw-r--r-- | rwlock.c | 434 | ||||
| -rw-r--r-- | tests/rwlock6.c | 110 | 
5 files changed, 543 insertions, 543 deletions
| @@ -1,3 +1,15 @@ +2001-02-07  Ross Johnson  <rpj@special.ise.canberra.edu.au> + +	* rwlock.c: Revamped. +	This implementation does not have reader/writer starvation problem. +	I've tried to make rwlock to behaive more like a normal mutex with +	races and scheduling policy determining who is more important; +	It also supports recursive locking, +	has less synchronization overhead (no broadcasts at all, +	readers are not blocked on any condition variable) and seem to +	be faster than the current implementation. +	- Alexander Terekhov <TEREKHOV@de.ibm.com> +  2001-02-06  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>  	* condvar.c (pthread_cond_init): Completely revamped. @@ -27,7 +27,7 @@  #include "implement.h"  static int -ptw32_cond_check_need_init(pthread_cond_t *cond) +ptw32_cond_check_need_init (pthread_cond_t *cond)  {    int result = 0; @@ -77,7 +77,7 @@ ptw32_cond_check_need_init(pthread_cond_t *cond)    LeaveCriticalSection(&ptw32_cond_test_init_lock); -  return(result); +  return result;  } @@ -124,7 +124,7 @@ pthread_condattr_init (pthread_condattr_t * attr)    *attr = attr_result; -  return (result); +  return result;  }                               /* pthread_condattr_init */ @@ -162,17 +162,16 @@ pthread_condattr_destroy (pthread_condattr_t * attr)    if (attr == NULL || *attr == NULL)      {        result = EINVAL; -      }    else      { -      free (*attr); +      (void) free (*attr);        *attr = NULL;        result = 0;      } -  return (result); +  return result;  }                               /* pthread_condattr_destroy */ @@ -220,13 +219,10 @@ pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared)  {    int result; -  if ((attr != NULL && *attr != NULL) && -      (pshared != NULL)) +  if ((attr != NULL && *attr != NULL) && (pshared != NULL))      { -        *pshared = (*attr)->pshared;        result = 0; -      }    else      { @@ -234,7 +230,7 @@ pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared)        result = EINVAL;      } -  return (result); +  return result;  }                               /* pthread_condattr_getpshared */ @@ -284,12 +280,10 @@ pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared)  {    int result; -  if ((attr != NULL && *attr != NULL) && -      ((pshared == PTHREAD_PROCESS_SHARED) || -       (pshared == PTHREAD_PROCESS_PRIVATE))) +  if ((attr != NULL && *attr != NULL) +      && ((pshared == PTHREAD_PROCESS_SHARED) +          || (pshared == PTHREAD_PROCESS_PRIVATE)))      { - -        if (pshared == PTHREAD_PROCESS_SHARED)          { @@ -306,19 +300,19 @@ pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared)          {            result = 0;          } -      (*attr)->pshared = pshared; +      (*attr)->pshared = pshared;      }    else      {        result = EINVAL; -      } -  return (result); +  return result;  }                               /* pthread_condattr_setpshared */ +  int  pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)       /* @@ -348,7 +342,7 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)        * ------------------------------------------------------        */  { -  int result = EAGAIN; +  int result;    pthread_cond_t cv = NULL;    if (cond == NULL) @@ -364,16 +358,15 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)         * processes.         */        result = ENOSYS; - -      goto FAIL0; +      goto DONE;      } -  cv = (pthread_cond_t) calloc (1, sizeof (*cv)); +  cv = (pthread_cond_t) calloc(1, sizeof (*cv));    if (cv == NULL)      {        result = ENOMEM; -      goto FAIL0; +      goto DONE;      }    cv->nWaitersBlocked   = 0; @@ -382,20 +375,21 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)    if (sem_init(&(cv->semBlockLock), 0, 1) != 0)      { +      result = errno;        goto FAIL0;      }    if (sem_init(&(cv->semBlockQueue), 0, 0) != 0)      { +      result = errno;        goto FAIL1;      } -  if (pthread_mutex_init(&(cv->mtxUnblockLock), 0) != 0) +  if ((result = pthread_mutex_init(&(cv->mtxUnblockLock), 0)) != 0)      {        goto FAIL2;      } -    result = 0;    goto DONE; @@ -412,13 +406,17 @@ FAIL1:    (void) sem_destroy(&(cv->semBlockLock));  FAIL0: +  (void) free(cv); +  cv = NULL; +  DONE:    *cond = cv; -  return (result); +  return result;  }                               /* pthread_cond_init */ +  int  pthread_cond_destroy (pthread_cond_t * cond)       /* @@ -447,13 +445,13 @@ pthread_cond_destroy (pthread_cond_t * cond)        * ------------------------------------------------------        */  { -  int result = 0;    pthread_cond_t cv; +  int result = 0, result1 = 0, result2 = 0;    /*     * Assuming any race condition here is harmless.     */ -  if (cond == NULL +  if (cond == NULL         || *cond == NULL)      {        return EINVAL; @@ -485,20 +483,34 @@ pthread_cond_destroy (pthread_cond_t * cond)         */        if (cv->nWaitersBlocked - cv->nWaitersUnblocked > 0)          { -          (void) sem_post(&(cv->semBlockLock)); -          (void) pthread_mutex_unlock(&(cv->mtxUnblockLock)); -          return EBUSY; +          if (sem_post(&(cv->semBlockLock)) != 0) +            { +              result = errno; +            } +          result1 = pthread_mutex_unlock(&(cv->mtxUnblockLock)); +          result2 = EBUSY; +        } +      else +        { +          /* +           * Now it is safe to destroy +           */ +          *cond = NULL; +          if (sem_destroy(&(cv->semBlockLock)) != 0) +            { +              result = errno; +            } +          if (sem_destroy(&(cv->semBlockQueue)) != 0) +            { +              result1 = errno; +            } +          if ((result2 = pthread_mutex_unlock(&(cv->mtxUnblockLock))) == 0) +            { +              result2 = pthread_mutex_destroy(&(cv->mtxUnblockLock)); +            } + +          (void) free(cv);          } - -      /* -       * Now it is safe to destroy -       */ -      *cond = NULL; /* Invalidate it before anything else */ -      (void) sem_destroy(&(cv->semBlockLock)); -      (void) sem_destroy(&(cv->semBlockQueue)); -      (void) pthread_mutex_unlock(&(cv->mtxUnblockLock)); -      (void) pthread_mutex_destroy(&(cv->mtxUnblockLock)); -      free(cv);      }    else      { @@ -532,7 +544,8 @@ pthread_cond_destroy (pthread_cond_t * cond)        LeaveCriticalSection(&ptw32_cond_test_init_lock);      } -  return (result); +    return ((result != 0) ? result : ((result1 != 0) ? result1 : result2)); +  }  /* @@ -548,17 +561,16 @@ typedef struct {  static void  ptw32_cond_wait_cleanup(void * args)  { -  ptw32_cond_wait_cleanup_args_t * cleanup_args = -    (ptw32_cond_wait_cleanup_args_t *) args; +  ptw32_cond_wait_cleanup_args_t * cleanup_args = (ptw32_cond_wait_cleanup_args_t *) args;    pthread_cond_t cv = cleanup_args->cv;    int * resultPtr = cleanup_args->resultPtr;    int eLastSignal; /* enum: 1=yes 0=no -1=cancelled/timedout w/o signal(s) */    int result;    /* -   * Whether we got here as a result of signal/broadcast or because of -   * timeout on wait or thread cancellation we indicate that we are no -   * longer waiting. The waiter is responsible for adjusting waiters +   * Whether we got here as a result of signal/broadcast or because of  +   * timeout on wait or thread cancellation we indicate that we are no  +   * longer waiting. The waiter is responsible for adjusting waiters      * (to)unblock(ed) counts (protected by unblock lock).     * Unblock lock/Sync.LEVEL-2 supports _timedwait and cancellation.     */ @@ -575,7 +587,7 @@ ptw32_cond_wait_cleanup(void * args)    /*     * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed     */ -  if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0) +  if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)       {        *resultPtr = result;        return; @@ -611,14 +623,15 @@ ptw32_cond_wait_cleanup(void * args)      }    /* -   * XSH: Upon successful return, the mutex has been locked and is owned +   * XSH: Upon successful return, the mutex has been locked and is owned      * by the calling thread     */    if ((result = pthread_mutex_lock(cleanup_args->mutexPtr)) != 0)      {        *resultPtr = result;      } -} + +}                               /* ptw32_cond_wait_cleanup */  static int  ptw32_cond_timedwait (pthread_cond_t * cond,  @@ -692,13 +705,13 @@ ptw32_cond_timedwait (pthread_cond_t * cond,         *              timeout, or         *              thread cancellation         * -       * Note: +       * Note:          *         *      ptw32_sem_timedwait is a cancellation point, -       *      hence providing the mechanism for making -       *      pthread_cond_wait a cancellation point. +       *      hence providing the mechanism for making  +       *      pthread_cond_wait a cancellation point.          *      We use the cleanup mechanism to ensure we -       *      re-lock the mutex and adjust (to)unblock(ed) waiters +       *      re-lock the mutex and adjust (to)unblock(ed) waiters          *      counts if we are cancelled, timed out or signalled.         */        if (ptw32_sem_timedwait(&(cv->semBlockQueue), abstime) != 0) @@ -712,18 +725,115 @@ ptw32_cond_timedwait (pthread_cond_t * cond,     */    pthread_cleanup_pop(1); -    /*     * "result" can be modified by the cleanup handler.     */ -  return (result); +  return result;  }                               /* ptw32_cond_timedwait */ +static int +ptw32_cond_unblock (pthread_cond_t * cond,  +                    int unblockAll) +{ +  int result; +  pthread_cond_t cv; + +  if (cond == NULL || *cond == NULL) +    { +      return EINVAL; +    } + +  cv = *cond; + +  /* +   * No-op if the CV is static and hasn't been initialised yet. +   * Assuming that any race condition is harmless. +   */ +  if (cv == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT) +    { +      return 0; +    } + +  /* +   * Synchronize access to waiters blocked count (LEVEL-1) +   */ +  if (sem_wait(&(cv->semBlockLock)) != 0) +    { +      return errno; +    } + +  /* +   * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2) +   * This sync.level supports _timedwait and cancellation +   */ +  if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0) +    { +      return result; +    } + +  /* +   * Adjust waiters blocked and unblocked counts (collect garbage) +   */ +  if (cv->nWaitersUnblocked != 0) +    { +      cv->nWaitersBlocked  -= cv->nWaitersUnblocked; +      cv->nWaitersUnblocked = 0; +    } + +  /* +   * If (after adjustment) there are still some waiters blocked counted... +   */ +  if ( cv->nWaitersBlocked > 0) +    { +      /* +       * We will unblock first waiter and leave semBlockLock/LEVEL-1 locked +       * LEVEL-1 access is left disabled until last signal/unblock completes +       */ +      cv->nWaitersToUnblock = (unblockAll) ? cv->nWaitersBlocked : 1; + +      /* +       * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed +       * This sync.level supports _timedwait and cancellation +       */ +      if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0) +        { +          return result; +        } + + +      /* +       * Now, with LEVEL-2 lock released let first waiter go through semaphore +       */ +      if (sem_post(&(cv->semBlockQueue)) != 0) +        { +          return errno; +        } +    } +  /* +   * No waiter blocked - no more LEVEL-1 access to blocked count needed... +   */ +  else if (sem_post(&(cv->semBlockLock)) != 0) +    { +      return errno; +    } +  /* +   * ...and no more LEVEL-2 access to waiters (to)unblock(ed) counts needed too +   * This sync.level supports _timedwait and cancellation +   */ +  else +    {   +      result = pthread_mutex_unlock(&(cv->mtxUnblockLock)); +    } + +  return result; + +}                               /* ptw32_cond_unblock */ +  int  pthread_cond_wait (pthread_cond_t * cond, -		   pthread_mutex_t * mutex) +                   pthread_mutex_t * mutex)       /*        * ------------------------------------------------------        * DOCPUBLIC @@ -748,8 +858,9 @@ pthread_cond_wait (pthread_cond_t * cond,        *      awakened by a signal or broadcast.        *        *      NOTES: +      *        *      1)      The function must be called with 'mutex' LOCKED -      *               by the calling thread, or undefined behaviour +      *              by the calling thread, or undefined behaviour        *              will result.        *        *      2)      This routine atomically releases 'mutex' and causes @@ -774,7 +885,7 @@ pthread_cond_wait (pthread_cond_t * cond,    /*     * The NULL abstime arg means INFINITE waiting.     */ -  return(ptw32_cond_timedwait(cond, mutex, NULL)); +  return (ptw32_cond_timedwait(cond, mutex, NULL));  }                               /* pthread_cond_wait */ @@ -808,7 +919,7 @@ pthread_cond_timedwait (pthread_cond_t * cond,        *        *      NOTES:        *      1)      The function must be called with 'mutex' LOCKED -      *               by the calling thread, or undefined behaviour +      *              by the calling thread, or undefined behaviour        *              will result.        *        *      2)      This routine atomically releases 'mutex' and causes @@ -828,118 +939,14 @@ pthread_cond_timedwait (pthread_cond_t * cond,        * ------------------------------------------------------        */  { -  int result = 0; -    if (abstime == NULL)      { -      result = EINVAL; -    } -  else -    { -      result = ptw32_cond_timedwait(cond, mutex, abstime); -    } - -  return(result); -}                               /* pthread_cond_timedwait */ - - -static int -ptw32_cond_unblock (pthread_cond_t * cond, -                    int unblockAll) -{ -  int result; -  pthread_cond_t cv; - -  if (cond == NULL || *cond == NULL) -    {        return EINVAL;      } -  cv = *cond; - -  /* -   * No-op if the CV is static and hasn't been initialised yet. -   * Assuming that any race condition is harmless. -   */ -  if (cv == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT) -    { -      return 0; -    } - -  /* -   * Synchronize access to waiters blocked count (LEVEL-1) -   */ -  if (sem_wait(&(cv->semBlockLock)) != 0) -    { -      return errno; -    } - -  /* -   * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2) -   * This sync.level supports _timedwait and cancellation -   */ -  if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0) -    { -      return result; -    } +  return (ptw32_cond_timedwait(cond, mutex, abstime)); -  /* -   * Adjust waiters blocked and unblocked counts (collect garbage) -   */ -  if (cv->nWaitersUnblocked != 0) -    { -      cv->nWaitersBlocked  -= cv->nWaitersUnblocked; -      cv->nWaitersUnblocked = 0; -    } - -  /* -   * If (after adjustment) there are still some waiters blocked counted... -   */ -  if ( cv->nWaitersBlocked > 0) -    { -      /* -       * We will unblock first waiter and leave semBlockLock/LEVEL-1 locked -       * LEVEL-1 access is left disabled until last signal/unblock completes -       */ -      cv->nWaitersToUnblock = (unblockAll) ? cv->nWaitersBlocked : 1; - -      /* -       * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed -       * This sync.level supports _timedwait and cancellation -       */ -      if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0) -        { -          return result; -        } - - -      /* -       * Now, with LEVEL-2 lock released let first waiter go through semaphore -       */ -      if (sem_post(&(cv->semBlockQueue)) != 0) -        { -          return errno; -        } -    } -  /* -   * No waiter blocked - no more LEVEL-1 access to blocked count needed... -   */ -  else if (sem_post(&(cv->semBlockLock)) != 0) -    { -      return errno; -    } -  /* -   * ...and no more LEVEL-2 access to waiters (to)unblock(ed) counts needed too -   * This sync.level supports _timedwait and cancellation -   */ -  else -    { -      result = pthread_mutex_unlock(&(cv->mtxUnblockLock)); -    } - -  return(result); - -}                               /* ptw32_cond_unblock */ +}                               /* pthread_cond_timedwait */  int @@ -966,17 +973,10 @@ pthread_cond_signal (pthread_cond_t * cond)        *      an unspecified waiter is awakened.        *        *      NOTES: +      *        *      1)      Use when any waiter can respond and only one need        *              respond (all waiters being equal).        * -      *      2)      This function MUST be called under the protection  -      *              of the SAME mutex that is used with the condition -      *              variable being signaled; OTHERWISE, the condition -      *              variable may be signaled between the test of the -      *              associated condition and the blocking -      *              pthread_cond_signal. -      *              This can cause an infinite wait. -      *        * RESULTS        *              0               successfully signaled condition,        *              EINVAL          'cond' is invalid, @@ -984,10 +984,10 @@ pthread_cond_signal (pthread_cond_t * cond)        * ------------------------------------------------------        */  { -  /*  +  /*     * The '0'(FALSE) unblockAll arg means unblock ONE waiter.     */ -  return(ptw32_cond_unblock(cond, 0)); +  return (ptw32_cond_unblock(cond, 0));  }                               /* pthread_cond_signal */ @@ -1009,14 +1009,8 @@ pthread_cond_broadcast (pthread_cond_t * cond)        *      all waiting threads.        *        *      NOTES: -      *      1)      This function MUST be called under the protection -      *              of the SAME mutex that is used with the condition -      *              variable being signaled; OTHERWISE, the condition -      *              variable may be signaled between the test of the -      *              associated condition and the blocking pthread_cond_wait. -      *              This can cause an infinite wait. -      * -      *      2)      Use when more than one waiter may respond to +      * +      *      1)      Use when more than one waiter may respond to        *              predicate change or if any waiting thread may        *              not be able to respond        * @@ -1029,10 +1023,9 @@ pthread_cond_broadcast (pthread_cond_t * cond)        * ------------------------------------------------------        */  { -  /*  +  /*     * The '1'(TRUE) unblockAll arg means unblock ALL waiters.     */ -  return(ptw32_cond_unblock(cond, 1)); - -} +  return (ptw32_cond_unblock(cond, 1)); +}                               /* pthread_cond_broadcast */ diff --git a/implement.h b/implement.h index ec889e3..353f4f9 100644 --- a/implement.h +++ b/implement.h @@ -46,13 +46,13 @@ typedef enum {     * The thread is still "alive" if the numeric value of the     * state is greater or equal "PThreadStateRunning".     */ -  PThreadStateInitial = 0,	/* Thread not running                   */ -  PThreadStateRunning,	        /* Thread alive & kicking               */ -  PThreadStateSuspended,	/* Thread alive but suspended           */ -  PThreadStateCanceling,	/* Thread alive but and is              */ +  PThreadStateInitial = 0,      /* Thread not running                   */ +  PThreadStateRunning,          /* Thread alive & kicking               */ +  PThreadStateSuspended,        /* Thread alive but suspended           */ +  PThreadStateCanceling,        /* Thread alive but and is              */                                  /* in the process of terminating        */                                  /* due to a cancellation request        */ -  PThreadStateException,	/* Thread alive but exiting             */ +  PThreadStateException,        /* Thread alive but exiting             */                                  /* due to an exception                  */    PThreadStateLast  } @@ -64,40 +64,14 @@ typedef enum {     * This enumeration represents the reason why a thread has     * terminated/is terminating.     */ -  PThreadDemisePeaceful = 0,	/* Death due natural causes     */ -  PThreadDemiseCancelled,	/* Death due to user cancel     */ -  PThreadDemiseException,	/* Death due to unhandled       */ +  PThreadDemisePeaceful = 0,    /* Death due natural causes     */ +  PThreadDemiseCancelled,       /* Death due to user cancel     */ +  PThreadDemiseException,       /* Death due to unhandled       */                                  /* exception                    */ -  PThreadDemiseNotDead	/* I'm not dead!                */ +  PThreadDemiseNotDead  /* I'm not dead!                */  }  PThreadDemise; - -/* - * ==================== - * ==================== - * Internal implementation of a critical section - * ==================== - * ==================== - */ - -typedef struct ptw32_cs_t_ { -  CRITICAL_SECTION cs; -  LONG lock_idx; -  int entered_count; -  int valid; -  pthread_t owner; -} ptw32_cs_t; - - -/* - * ==================== - * ==================== - * POSIX thread and attributes - * ==================== - * ==================== - */ -  struct pthread_t_ {    DWORD thread;    HANDLE threadH; @@ -148,7 +122,8 @@ struct pthread_attr_t_ {  #define PTW32_OBJECT_INVALID   NULL  struct pthread_mutex_t_ { -  ptw32_cs_t cs; +  HANDLE mutex; +  CRITICAL_SECTION cs;    int lockCount;    pthread_t ownerThread;  }; @@ -156,7 +131,7 @@ struct pthread_mutex_t_ {  struct pthread_mutexattr_t_ {    int pshared; -  int type; +  int forcecs;  }; @@ -177,54 +152,36 @@ struct ThreadParms {    void *arg;  }; -#if 0 -struct pthread_cond_t_ { -  long waiters;                       /* # waiting threads             */ -  pthread_mutex_t waitersLock;        /* Mutex that guards access to  -					 waiter count                  */ -  sem_t sema;                         /* Queue up threads waiting for the  -					 condition to become signaled  */ -  HANDLE waitersDone;                 /* An auto reset event used by the  -					 broadcast/signal thread to wait  -					 for the waiting thread(s) to wake -					 up and get a chance at the   -					 semaphore                     */ -  int wasBroadcast;                   /* keeps track if we are signaling  -					 or broadcasting               */ -}; - -#else  struct pthread_cond_t_ { -  long            nWaitersBlocked;   /* Number of threads blocked */ -  long            nWaitersUnblocked; /* Number of threads unblocked */ -  long            nWaitersToUnblock; /* Number of threads to unblock */ -  sem_t           semBlockQueue;     /* Queue up threads waiting for the */ -                                     /*   condition to become signalled */ -  sem_t           semBlockLock;      /* Semaphore that guards access to */ -                                     /* waiters blocked count/block queue */ -                                     /* +-> Mandatory Sync.LEVEL-1 */ -  pthread_mutex_t mtxUnblockLock;    /* Mutex that guards access to */ -                                     /* waiters (to)unblock(ed) counts */ -                                     /* +-> Optional* Sync.LEVEL-2 */ -};                                   /* Opt*) for _timedwait and cancellation */ -#endif +  long            nWaitersBlocked;   /* Number of threads blocked            */ +  long            nWaitersUnblocked; /* Number of threads unblocked          */ +  long            nWaitersToUnblock; /* Number of threads to unblock         */ +  sem_t           semBlockQueue;     /* Queue up threads waiting for the     */ +                                     /*   condition to become signalled      */ +  sem_t           semBlockLock;      /* Semaphore that guards access to      */ +                                     /* | waiters blocked count/block queue  */ +                                     /* +-> Mandatory Sync.LEVEL-1           */ +  pthread_mutex_t mtxUnblockLock;    /* Mutex that guards access to          */ +                                     /* | waiters (to)unblock(ed) counts     */ +                                     /* +-> Optional* Sync.LEVEL-2           */ +};                                   /* Opt*) for _timedwait and cancellation*/ +  struct pthread_condattr_t_ {    int pshared;  }; -#define RW_MAGIC    0x19283746 +#define PTW32_RWLOCK_MAGIC 0xfacade2  struct pthread_rwlock_t_ { -    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 */ -    int             rw_nwaitreaders; /* the number waiting */ -    int             rw_nwaitwriters; /* the number waiting */ -    int             rw_refcount;     /* -1 if writer has the lock, -                                        else # readers holding the lock */ +  pthread_mutex_t mtxExclusiveAccess; +  pthread_mutex_t mtxSharedAccessCompleted; +  pthread_cond_t  cndSharedAccessCompleted; +  int             nSharedAccessCount; +  int             nExclusiveAccessCount; +  int             nCompletedSharedAccessCount; +  int             nMagic;  };  struct pthread_rwlockattr_t_ { @@ -307,17 +264,17 @@ struct ThreadKeyAssoc {   * Severity Values:   */  #define SE_SUCCESS              0x00 -#define SE_INFORMATION	        0x01 +#define SE_INFORMATION          0x01  #define SE_WARNING              0x02  #define SE_ERROR                0x03  #define MAKE_SOFTWARE_EXCEPTION( _severity, _facility, _exception ) \ -( (DWORD) ( ( (_severity) << 30 ) |	/* Severity code	*/ \ -	    ( 1 << 29 )	|		/* MS=0, User=1		*/ \ -	    ( 0 << 28 )	|		/* Reserved		*/ \ -	    ( (_facility) << 16 ) |	/* Facility Code	*/ \ -	    ( (_exception) <<  0 )	/* Exception Code	*/ \ -	    ) ) +( (DWORD) ( ( (_severity) << 30 ) |     /* Severity code        */ \ +            ( 1 << 29 ) |               /* MS=0, User=1         */ \ +            ( 0 << 28 ) |               /* Reserved             */ \ +            ( (_facility) << 16 ) |     /* Facility Code        */ \ +            ( (_exception) <<  0 )      /* Exception Code       */ \ +            ) )  /*   * We choose one specific Facility/Error code combination to @@ -325,13 +282,13 @@ struct ThreadKeyAssoc {   * We store our actual component and error code within   * the optional information array.   */ -#define EXCEPTION_PTW32_SERVICES	\ +#define EXCEPTION_PTW32_SERVICES        \       MAKE_SOFTWARE_EXCEPTION( SE_ERROR, \ -			      PTW32_SERVICES_FACILITY, \ -			      PTW32_SERVICES_ERROR ) +                              PTW32_SERVICES_FACILITY, \ +                              PTW32_SERVICES_ERROR ) -#define PTW32_SERVICES_FACILITY		0xBAD -#define PTW32_SERVICES_ERROR	       	0xDEED +#define PTW32_SERVICES_FACILITY         0xBAD +#define PTW32_SERVICES_ERROR            0xDEED  #endif /* _MSC_VER */ @@ -394,13 +351,13 @@ ptw32_threadStart (ThreadParms * threadParms);  void ptw32_callUserDestroyRoutines (pthread_t thread);  int ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, -			    pthread_t thread, -			    pthread_key_t key); +                            pthread_t thread, +                            pthread_key_t key);  void ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc);  int ptw32_sem_timedwait (sem_t * sem, -			    const struct timespec * abstime); +                            const struct timespec * abstime);  #ifdef NEED_SEM  void ptw32_decrease_semaphore(sem_t * sem); @@ -428,17 +385,17 @@ BOOL ptw32_increase_semaphore(sem_t * sem,   */  #define _beginthreadex(security, \ -		       stack_size, \ -		       start_proc, \ -		       arg, \ -		       flags, \ -		       pid) \ +                       stack_size, \ +                       start_proc, \ +                       arg, \ +                       flags, \ +                       pid) \          CreateThread(security, \ -		     stack_size, \ -		     (LPTHREAD_START_ROUTINE) start_proc, \ -		     arg, \ -		     flags, \ -		     pid) +                     stack_size, \ +                     (LPTHREAD_START_ROUTINE) start_proc, \ +                     arg, \ +                     flags, \ +                     pid)  #define _endthreadex ExitThread @@ -24,6 +24,7 @@   */  #include <errno.h> +#include <limits.h>  #include "pthread.h"  #include "implement.h" @@ -80,120 +81,140 @@ ptw32_rwlock_check_need_init(pthread_rwlock_t *rwlock)    LeaveCriticalSection(&ptw32_rwlock_test_init_lock); -  return(result); +  return result;  }  int  pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)  { -    int result = 0; -    pthread_rwlock_t rw; +    int result; +    pthread_rwlock_t rwl = 0;      if (rwlock == NULL)        {          return EINVAL;        } -    rw = (pthread_rwlock_t) calloc(1, sizeof(*rw)); +    if (attr != NULL && *attr != NULL) +      { +        result = EINVAL; /* Not supported */ +        goto DONE; +      } + +    rwl = (pthread_rwlock_t) calloc(1, sizeof(*rwl)); -    if (rw == NULL) +    if (rwl == NULL)        {          result = ENOMEM; -        goto FAIL0; +        goto DONE;        } -    if (attr != NULL -        && *attr != NULL) +    rwl->nSharedAccessCount = 0; +    rwl->nExclusiveAccessCount = 0; +    rwl->nCompletedSharedAccessCount = 0; + +    result = pthread_mutex_init(&rwl->mtxExclusiveAccess, NULL); +    if (result != 0)        { -        result = EINVAL; /* Not supported */          goto FAIL0;        } -    if ((result = pthread_mutex_init(&(rw->rw_lock), NULL)) != 0) +    result = pthread_mutex_init(&rwl->mtxSharedAccessCompleted, NULL); +    if (result != 0)        {          goto FAIL1;        } -    if ((result = pthread_cond_init(&(rw->rw_condreaders), NULL)) != 0) +    result = pthread_cond_init(&rwl->cndSharedAccessCompleted, NULL); +    if (result != 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; +    rwl->nMagic = PTW32_RWLOCK_MAGIC;      result = 0; -    goto FAIL0; - -FAIL3: -    (void) pthread_cond_destroy(&(rw->rw_condreaders)); +    goto DONE;  FAIL2: -    (void) pthread_mutex_destroy(&(rw->rw_lock)); +    (void) pthread_mutex_destroy(&(rwl->mtxSharedAccessCompleted));  FAIL1: +    (void) pthread_mutex_destroy(&(rwl->mtxExclusiveAccess)); +  FAIL0: -    *rwlock = rw; +    (void) free(rwl); +    rwl = NULL; + +DONE: +    *rwlock = rwl; -    return(result); +    return result;  }  int  pthread_rwlock_destroy(pthread_rwlock_t *rwlock)  { -    pthread_rwlock_t rw; -    int result = 0; +    pthread_rwlock_t rwl; +    int result = 0, result1 = 0, result2 = 0;      if (rwlock == NULL || *rwlock == NULL)        { -        return(EINVAL); +        return EINVAL;        }      if (*rwlock != (pthread_rwlock_t) PTW32_OBJECT_AUTO_INIT)        { -        rw = *rwlock; +        rwl = *rwlock; -        if (pthread_mutex_lock(&(rw->rw_lock)) != 0) +        if (rwl->nMagic != PTW32_RWLOCK_MAGIC)            { -            return(EINVAL); +            return EINVAL;            } -        if (rw->rw_magic != RW_MAGIC) +        if ((result = pthread_mutex_lock(&(rwl->mtxExclusiveAccess))) != 0)            { -            (void) pthread_mutex_unlock(&(rw->rw_lock)); -            return(EINVAL); +            return result;            } -        if (rw->rw_refcount != 0 -            || rw->rw_nwaitreaders != 0 -            || rw->rw_nwaitwriters != 0) +        if ((result = pthread_mutex_lock(&(rwl->mtxSharedAccessCompleted))) != 0)            { -            (void) pthread_mutex_unlock(&(rw->rw_lock)); -            result = EBUSY; +            (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +            return result;            } -        else -          { -            /* -             * Need to NULL this before we start freeing up -             * and destroying it's components. -             */ -            *rwlock = NULL; -            rw->rw_magic = 0; -            (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); -           } +        /* +         * Check whether any threads own/wait for the lock (wait for ex.access); +         * report "BUSY" if so. +         */ +        if (rwl->nExclusiveAccessCount > 0 +            || rwl->nSharedAccessCount > rwl->nCompletedSharedAccessCount) +          { +            result = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted)); +            result1 = pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +            result2 = EBUSY; +          } +        else  +          { +            rwl->nMagic = 0; + +            if ((result = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted))) != 0) +              { +                pthread_mutex_unlock(&rwl->mtxExclusiveAccess); +                return result; +              } + +            if ((result = pthread_mutex_unlock(&(rwl->mtxExclusiveAccess))) != 0) +              { +                return result; +              } + +            *rwlock = NULL; /* Invalidate rwlock before anything else */ +            result = pthread_cond_destroy(&(rwl->cndSharedAccessCompleted)); +            result1 = pthread_mutex_destroy(&(rwl->mtxSharedAccessCompleted)); +            result2 = pthread_mutex_destroy(&(rwl->mtxExclusiveAccess)); +            (void) free(rwl); +          }        }      else        { @@ -227,28 +248,18 @@ pthread_rwlock_destroy(pthread_rwlock_t *rwlock)          LeaveCriticalSection(&ptw32_rwlock_test_init_lock);        } -    return(result); -} - -static void -ptw32_rwlock_cancelrdwait(void * arg) -{ -    pthread_rwlock_t rw; - -    rw = (pthread_rwlock_t) arg; -    rw->rw_nwaitreaders--; -    pthread_mutex_unlock(&(rw->rw_lock)); +    return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));  }  int  pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)  { -    int result = 0; -    pthread_rwlock_t rw; +    int result; +    pthread_rwlock_t rwl;      if (rwlock == NULL || *rwlock == NULL)        { -        return(EINVAL); +        return EINVAL;        }      /* @@ -263,70 +274,64 @@ pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)          if (result != 0 && result != EBUSY)            { -            return(result); +            return result;            }        } -    rw = *rwlock; +    rwl = *rwlock; -    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +    if (rwl->nMagic != PTW32_RWLOCK_MAGIC)        { -        return(result); +        return EINVAL;        } -    if (rw->rw_magic != RW_MAGIC) +    if ((result = pthread_mutex_lock(&(rwl->mtxExclusiveAccess))) != 0)        { -        result = EINVAL; -        goto FAIL1; +        return result;        } -    /* -     * Give preference to waiting writers -     */ -    while (rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0) +    if (++rwl->nSharedAccessCount == INT_MAX)        { -        rw->rw_nwaitreaders++; -        pthread_cleanup_push(ptw32_rwlock_cancelrdwait, rw); -        result = pthread_cond_wait(&(rw->rw_condreaders), &(rw->rw_lock)); -        pthread_cleanup_pop(0); -        rw->rw_nwaitreaders--; - -        if (result != 0) +        if ((result = pthread_mutex_lock(&(rwl->mtxSharedAccessCompleted))) != 0)            { -            break; +            (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +            return result;            } -      } -    if (result == 0) -      { -        rw->rw_refcount++; /* another reader has a read lock */ -      } +        rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; +        rwl->nCompletedSharedAccessCount = 0; -FAIL1: -    (void) pthread_mutex_unlock(&(rw->rw_lock)); +        if ((result = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted))) != 0) +          { +            (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +            return result; +          } +      } -    return(result); +    return (pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)));  }  static void  ptw32_rwlock_cancelwrwait(void * arg)  { -    pthread_rwlock_t rw; +    pthread_rwlock_t rwl = (pthread_rwlock_t) arg; -    rw = (pthread_rwlock_t) arg; -    rw->rw_nwaitwriters--; -    (void) pthread_mutex_unlock(&(rw->rw_lock)); +    rwl->nSharedAccessCount = -rwl->nCompletedSharedAccessCount; +    rwl->nCompletedSharedAccessCount = 0; + +    (void) pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted)); +    (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess));  }  int  pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)  {      int result; -    pthread_rwlock_t rw; +    pthread_rwlock_t rwl;      if (rwlock == NULL || *rwlock == NULL)        { -        return(EINVAL); +        return EINVAL;        }      /* @@ -341,51 +346,71 @@ pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)          if (result != 0 && result != EBUSY)            { -            return(result); +            return result;            }        } -    rw = *rwlock; +    rwl = *rwlock; -    if (rw->rw_magic != RW_MAGIC) +    if (rwl->nMagic != RWLOCK_MAGIC)        { -        return(EINVAL); +        return EINVAL; +      } + +    if ((result = pthread_mutex_lock(&(rwl->mtxExclusiveAccess))) != 0) +      { +        return result;        } -    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +    if ((result = pthread_mutex_lock(&(rwl->mtxSharedAccessCompleted))) != 0)        { -        return(result); +        (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +        return result;        } -    while (rw->rw_refcount != 0) +    if (rwl->nExclusiveAccessCount == 0)         { -        rw->rw_nwaitwriters++; -        pthread_cleanup_push(ptw32_rwlock_cancelwrwait, rw); -        result = pthread_cond_wait(&(rw->rw_condwriters), &(rw->rw_lock)); -        pthread_cleanup_pop(0); -        rw->rw_nwaitwriters--; +        if (rwl->nCompletedSharedAccessCount > 0)  +          { +            rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; +            rwl->nCompletedSharedAccessCount = 0; +          } -        if (result != 0) +        if (rwl->nSharedAccessCount > 0)             { -            break; +            rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount; + +            pthread_cleanup_push(ptw32_rwlock_cancelwrwait, (void*)rwl); + +            do  +              { +                result = pthread_cond_wait(&(rwl->cndSharedAccessCompleted),  +                                           &(rwl->mtxSharedAccessCompleted)); +              } +            while (result == 0 && rwl->nCompletedSharedAccessCount < 0); + +            pthread_cleanup_pop ((result != 0) ? 1 : 0); + +            if (result == 0) +              { +                rwl->nSharedAccessCount = 0; +              }            }        }      if (result == 0)        { -        rw->rw_refcount = -1; +        rwl->nExclusiveAccessCount++;        } -    (void) pthread_mutex_unlock(&(rw->rw_lock)); - -    return(result); +    return result;  }  int  pthread_rwlock_unlock(pthread_rwlock_t * rwlock)  { -    int result = 0; -    pthread_rwlock_t rw; +    int result, result1; +    pthread_rwlock_t rwl;      if (rwlock == NULL || *rwlock == NULL)        { @@ -397,67 +422,51 @@ pthread_rwlock_unlock(pthread_rwlock_t * rwlock)          /*           * Assume any race condition here is harmless.           */ -        return(0); +        return 0;        } -    rw = *rwlock; - -    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) -      { -        return(result); -      } +    rwl = *rwlock; -    if (rw->rw_magic != RW_MAGIC) +    if (rwl->nMagic != PTW32_RWLOCK_MAGIC)        { -        result = EINVAL; -        goto FAIL1; +        return EINVAL;        } -    if (rw->rw_refcount > 0) -      { -        rw->rw_refcount--;             /* releasing a reader */ -      } -    else if (rw->rw_refcount == -1) +    if (rwl->nExclusiveAccessCount == 0)         { -        rw->rw_refcount = 0;           /* releasing a writer */ -      } -    else  -      { -        return(EINVAL); -      } - -    result = 0; +        if ((result = pthread_mutex_lock(&(rwl->mtxSharedAccessCompleted))) != 0) +          { +            return result; +          } -    /* -     * Give preference to waiting writers over waiting readers -     */ -    if (rw->rw_nwaitwriters > 0) -      { -        if (rw->rw_refcount == 0) +        if (++rwl->nCompletedSharedAccessCount == 0)            { -            result = pthread_cond_signal(&(rw->rw_condwriters)); +            result = pthread_cond_signal(&(rwl->cndSharedAccessCompleted));            } + +        result1 = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted));        } -    else if (rw->rw_nwaitreaders > 0) +    else         { -         result = pthread_cond_broadcast(&(rw->rw_condreaders)); -      } +        rwl->nExclusiveAccessCount--; -FAIL1: -    (void) pthread_mutex_unlock(&(rw->rw_lock)); +        result = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted)); +        result1 = pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); -    return(result); -} +      } +    return ((result != 0) ? result : result1); +} +  int  pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)  { -    int result = 0; -    pthread_rwlock_t rw; +    int result; +    pthread_rwlock_t rwl;      if (rwlock == NULL || *rwlock == NULL)        { -        return(EINVAL); +        return EINVAL;        }      /* @@ -472,47 +481,52 @@ pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)          if (result != 0 && result != EBUSY)            { -            return(result); +            return result;            }        } -    rw = *rwlock; +    rwl = *rwlock; -    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +    if (rwl->nMagic != PTW32_RWLOCK_MAGIC)        { -        return(result); +        return EINVAL;        } -    if (rw->rw_magic != RW_MAGIC) +    if ((result = pthread_mutex_trylock(&(rwl->mtxExclusiveAccess))) != 0)        { -        result = EINVAL; -        goto FAIL1; +        return result;        } -    if (rw->rw_refcount == -1 || rw->rw_nwaitwriters > 0) -      { -        result = EBUSY;    /* held by a writer or waiting writers */ -      } -    else +    if (++rwl->nSharedAccessCount == INT_MAX)         { -        rw->rw_refcount++; /* increment count of reader locks */ -      } +        if ((result = pthread_mutex_lock(&(rwl->mtxSharedAccessCompleted))) != 0) +          { +            (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +            return result; +          } -FAIL1: -    (void) pthread_mutex_unlock(&(rw->rw_lock)); +        rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; +        rwl->nCompletedSharedAccessCount = 0; -    return(result); +        if ((result = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted))) != 0) +          { +            (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +            return result; +          } +      } + +    return (pthread_mutex_unlock(&rwl->mtxExclusiveAccess));  }  int  pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)  { -    int result = 0; -    pthread_rwlock_t rw; +    int result, result1; +    pthread_rwlock_t rwl;      if (rwlock == NULL || *rwlock == NULL)        { -        return(EINVAL); +        return EINVAL;        }      /* @@ -527,34 +541,58 @@ pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)          if (result != 0 && result != EBUSY)            { -            return(result); +            return result;            }        } -    rw = *rwlock; +    rwl = *rwlock; -    if ((result = pthread_mutex_lock(&(rw->rw_lock))) != 0) +    if (rwl->nMagic != PTW32_RWLOCK_MAGIC)        { -        return(result); +        return EINVAL;        } -    if (rw->rw_magic != RW_MAGIC) +    if ((result = pthread_mutex_trylock(&(rwl->mtxExclusiveAccess))) != 0)        { -        result = EINVAL; -        goto FAIL1; +        return result;        } -    if (rw->rw_refcount != 0) +    if ((result = pthread_mutex_trylock(&(rwl->mtxSharedAccessCompleted))) != 0)        { -        result = EBUSY;       /* held by either writer or reader(s) */ +        result1 = pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +        return ((result1 != 0) ? result1 : result);        } -    else + +    if (rwl->nExclusiveAccessCount == 0)         { -        rw->rw_refcount = -1; /* available, indicate a writer has it */ -      } +        if (rwl->nCompletedSharedAccessCount > 0)  +          { +            rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; +            rwl->nCompletedSharedAccessCount = 0; +          } -FAIL1: -    (void) pthread_mutex_unlock(&(rw->rw_lock)); +        if (rwl->nSharedAccessCount > 0)  +          { +            if ((result = pthread_mutex_unlock(&(rwl->mtxSharedAccessCompleted))) != 0) +              { +                (void) pthread_mutex_unlock(&(rwl->mtxExclusiveAccess)); +                return result; +              } + +            if ((result = pthread_mutex_unlock(&(rwl->mtxExclusiveAccess))) == 0) +              { +                result = EBUSY; +              } +          } +        else +          { +            rwl->nExclusiveAccessCount = 1; +          } +      } +    else  +      { +        result = EBUSY; +      } -    return(result); +    return result;  } diff --git a/tests/rwlock6.c b/tests/rwlock6.c index 7daccd7..8a75b83 100644 --- a/tests/rwlock6.c +++ b/tests/rwlock6.c @@ -1,65 +1,65 @@ -/*  - * rwlock6.c - * - * Check that writer locks have priority. - * - * Depends on API functions:  - *	pthread_rwlock_rdlock() - *	pthread_rwlock_wrlock() - *	pthread_rwlock_unlock() - */ +/* 
 + * rwlock6.c
 + *
 + * Check that writer locks have priority.
 + *
 + * Depends on API functions: 
 + *      pthread_rwlock_rdlock()
 + *      pthread_rwlock_wrlock()
 + *      pthread_rwlock_unlock()
 + */
 -#include "test.h" -  -static pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER; +#include "test.h"
 + 
 +static pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
 -static int bankAccount; +static int bankAccount;
 -void * wrfunc(void * arg) -{ -  assert(pthread_rwlock_wrlock(&rwlock1) == 0); -  Sleep(1000); -  bankAccount += 10; -  assert(pthread_rwlock_unlock(&rwlock1) == 0); +void * wrfunc(void * arg)
 +{
 +  assert(pthread_rwlock_wrlock(&rwlock1) == 0);
 +  Sleep(1000);
 +  bankAccount += 10;
 +  assert(pthread_rwlock_unlock(&rwlock1) == 0);
 -  return((void *) bankAccount);  -} -  -void * rdfunc(void * arg) -{ -  int ba = 0; +  return((void *) bankAccount); 
 +}
 + 
 +void * rdfunc(void * arg)
 +{
 +  int ba = 0;
 -  assert(pthread_rwlock_rdlock(&rwlock1) == 0); -  ba = bankAccount; -  assert(pthread_rwlock_unlock(&rwlock1) == 0); +  assert(pthread_rwlock_rdlock(&rwlock1) == 0);
 +  ba = bankAccount;
 +  assert(pthread_rwlock_unlock(&rwlock1) == 0);
 -  ba += 10; -  return((void *) ba);  -} -  -int -main() -{ -  pthread_t wrt1; -  pthread_t wrt2; -  pthread_t rdt; -  int wr1Result = 0; -  int wr2Result = 0; -  int rdResult = 0; +  ba += 10;
 +  return((void *) ba); 
 +}
 + 
 +int
 +main()
 +{
 +  pthread_t wrt1;
 +  pthread_t wrt2;
 +  pthread_t rdt;
 +  int wr1Result = 0;
 +  int wr2Result = 0;
 +  int rdResult = 0;
 -  assert(pthread_create(&wrt1, NULL, wrfunc, NULL) == 0); -  Sleep(200); -  assert(pthread_create(&rdt, NULL, rdfunc, NULL) == 0); -  Sleep(200); -  assert(pthread_create(&wrt2, NULL, wrfunc, NULL) == 0); +  assert(pthread_create(&wrt1, NULL, wrfunc, NULL) == 0);
 +  Sleep(200);
 +  assert(pthread_create(&rdt, NULL, rdfunc, NULL) == 0);
 +  Sleep(200);
 +  assert(pthread_create(&wrt2, NULL, wrfunc, NULL) == 0);
 -  assert(pthread_join(wrt1, (void **) &wr1Result) == 0); -  assert(pthread_join(wrt2, (void **) &wr2Result) == 0); -  assert(pthread_join(rdt, (void **) &rdResult) == 0); +  assert(pthread_join(wrt1, (void **) &wr1Result) == 0);
 +  assert(pthread_join(wrt2, (void **) &wr2Result) == 0);
 +  assert(pthread_join(rdt, (void **) &rdResult) == 0);
 -  assert(wr1Result == 10); -  assert(wr2Result == 20); -  assert(rdResult == 30); +  assert(wr1Result == 10);
 +  assert(wr2Result == 20);
 +  assert(rdResult == 20);
 -  return 0; -} +  return 0;
 +}
 | 
