From 2f4a1905d1a8c424900a8615ec730f7637482525 Mon Sep 17 00:00:00 2001 From: rpj Date: Wed, 7 Feb 2001 03:48:23 +0000 Subject: Revamp read-write locks and update cond vars. See ChangeLog. --- ChangeLog | 12 ++ condvar.c | 371 ++++++++++++++++++++++++------------------------ implement.h | 159 ++++++++------------- rwlock.c | 434 ++++++++++++++++++++++++++++++-------------------------- tests/rwlock6.c | 110 +++++++------- 5 files changed, 543 insertions(+), 543 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0606071..074526f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2001-02-07 Ross Johnson + + * 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 + 2001-02-06 Ross Johnson * condvar.c (pthread_cond_init): Completely revamped. diff --git a/condvar.c b/condvar.c index 2974000..99871f9 100644 --- a/condvar.c +++ b/condvar.c @@ -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 diff --git a/rwlock.c b/rwlock.c index cd886a4..4e15636 100644 --- a/rwlock.c +++ b/rwlock.c @@ -24,6 +24,7 @@ */ #include +#include #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; +} -- cgit v1.2.3