summaryrefslogtreecommitdiff
path: root/rwlock.c
diff options
context:
space:
mode:
authorrpj <rpj>2001-05-31 02:01:47 +0000
committerrpj <rpj>2001-05-31 02:01:47 +0000
commite121b938c9f012958196a3141f04a3fd4f58bdb9 (patch)
treed1cb950413e3a350606f2a4d9bea687b6680570d /rwlock.c
parent6bf07e836550f9ffe11e0f38ff1323be731eb250 (diff)
2001-05-30 Ross Johnson <rpj@setup1.ise.canberra.edu.au>
* pthread.h (rand_r): Fake using _seed argument to quell compiler warning (compiler should optimise this away later). * GNUmakefile (OPT): Leave symbolic information out of the library and increase optimisation level - for smaller faster prebuilt dlls. 2001-05-29 Ross Johnson <rpj@setup1.ise.canberra.edu.au> Contributed by - Milan Gardian <Milan.Gardian@LEIBINGER.com> * Makefile: fix typo. * pthreads.h: Fix problems with stdcall/cdecl conventions, in particular remove the need for PT_STDCALL everywhere; remove warning supression. * (errno): Fix the longstanding "inconsistent dll linkage" problem with errno; now also works with /MD debugging libs - warnings emerged when compiling pthreads library with /MD (or /MDd) compiler switch, instead of /MT (or /MTd) (i.e. when compiling pthreads using Multithreaded DLL CRT instead of Multithreaded statically linked CRT). * create.c (pthread_create): Likewise; fix typo. * private.c (ptw32_threadStart): Eliminate use of terminate() which doesn't throw exceptions. * Remove unnecessary #includes from a number of modules - [I had to #include malloc.h in implement.h for gcc - rpj]. 2001-05-29 Ross Johnson <rpj@setup1.ise.canberra.edu.au> Contributed by - Thomas Pfaff <tpfaff@gmx.net> * pthread.h (PTHREAD_MUTEX_DEFAULT): New; equivalent to PTHREAD_MUTEX_DEFAULT_NP. * (PTHREAD_MUTEX_NORMAL): Similarly. * (PTHREAD_MUTEX_ERRORCHECK): Similarly. * (PTHREAD_MUTEX_RECURSIVE): Similarly. * (pthread_mutex_setdefaultkind_np): New; Linux compatibility stub for pthread_mutexattr_settype. * (pthread_mutexattr_getkind_np): New; Linux compatibility stub for pthread_mutexattr_gettype. * mutex.c (pthread_mutexattr_settype): New; allow the following types of mutex: PTHREAD_MUTEX_DEFAULT_NP PTHREAD_MUTEX_NORMAL_NP PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_RECURSIVE_NP * Note that PTHREAD_MUTEX_DEFAULT is equivalent to PTHREAD_MUTEX_NORMAL - ie. mutexes should no longer be recursive by default, and a thread will deadlock if it tries to relock a mutex it already owns. This is inline with other pthreads implementations. * (pthread_mutex_lock): Process the lock request according to the mutex type. * (pthread_mutex_init): Eliminate use of Win32 mutexes as the basis of POSIX mutexes - instead, a combination of one critical section and one semaphore are used in conjunction with Win32 Interlocked* routines. * (pthread_mutex_destroy): Likewise. * (pthread_mutex_lock): Likewise. * (pthread_mutex_trylock): Likewise. * (pthread_mutex_unlock): Likewise. * Use longjmp/setjmp to implement cancelation when building the library using a C compiler which doesn't support exceptions, e.g. gcc -x c (note that gcc -x c++ uses exceptions). * Also fixed some of the same typos and eliminated PT_STDCALL as Milan Gardian's patches above. 2001-02-07 Ross Johnson <rpj@special.ise.canberra.edu.au> Contributed by - Alexander Terekhov <TEREKHOV@de.ibm.com> * rwlock.c: Revamped. * implement.h (pthread_rwlock_t_): Redefined. This implementation does not have reader/writer starvation problem. Rwlock attempts to behave 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 [W98 appears to be approximately 15 percent faster at least - on top of speed increase from Thomas Pfaff's changes to mutex.c - rpj].
Diffstat (limited to 'rwlock.c')
-rw-r--r--rwlock.c436
1 files changed, 237 insertions, 199 deletions
diff --git a/rwlock.c b/rwlock.c
index 302d77b..cc85b7f 100644
--- a/rwlock.c
+++ b/rwlock.c
@@ -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
-_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(_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
-_rwlock_cancelwrwait(void * arg)
+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 != PTW32_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(_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;
}