Index: thread/thr_rwlock.c =================================================================== RCS file: /home/ncvs/src/lib/libthr/thread/thr_rwlock.c,v retrieving revision 1.10 diff -u -r1.10 thr_rwlock.c --- thread/thr_rwlock.c 23 Apr 2006 11:23:37 -0000 1.10 +++ thread/thr_rwlock.c 23 Mar 2008 07:13:08 -0000 @@ -34,9 +34,13 @@ #include #include "un-namespace.h" #include "thr_private.h" +#include /* maximum number of times a read lock may be obtained */ -#define MAX_READ_LOCKS (INT_MAX - 1) +#define MAX_READ_LOCKS 0x3fffffff +#define WRITE_WAITERS 0x40000000 +#define WRITE_OWNER -1 +#define READ_COUNT(s) ((s) & WRITE_WAITERS) __weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy); __weak_reference(_pthread_rwlock_init, pthread_rwlock_init); @@ -147,6 +151,7 @@ { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; + int state; int ret; if (rwlock == NULL) @@ -161,34 +166,45 @@ prwlock = *rwlock; } + for (;;) { + for (;;) { + state = (volatile int)prwlock->state; + if (state < 0) + break; + if (READ_COUNT(state) == MAX_READ_LOCKS) + return (EAGAIN); + /* + * To avoid having to track all the rdlocks held by + * a thread or all of the threads that hold a rdlock, + * we keep a simple count of all the rdlocks held by + * a thread. If a thread holds any rdlocks it is + * possible that it is attempting to take a recursive + * rdlock. If there are blocked writers and precedence + * is given to them, then that would result in the thread + * deadlocking. So allowing a thread to take the rdlock + * when it already has one or more rdlocks avoids the + * deadlock. I hope the reader can follow that logic ;-) + */ + if (state & WRITE_WAITERS && curthread->rdlock_count == 0) + break; + /* indicate we are locked for reading */ + if (atomic_cmpset_acq_int((volatile int *)&prwlock->state, state, state + 1)) { + curthread->rdlock_count++; + return (0); + } + } + /* grab the monitor lock */ + if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) + return (ret); + state = prwlock->state; + if (state >= 0) { + _pthread_mutex_unlock(&prwlock->lock); + continue; + } - /* grab the monitor lock */ - if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) - return (ret); - - /* check lock count */ - if (prwlock->state == MAX_READ_LOCKS) { - _pthread_mutex_unlock(&prwlock->lock); - return (EAGAIN); - } - - if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { - /* - * To avoid having to track all the rdlocks held by - * a thread or all of the threads that hold a rdlock, - * we keep a simple count of all the rdlocks held by - * a thread. If a thread holds any rdlocks it is - * possible that it is attempting to take a recursive - * rdlock. If there are blocked writers and precedence - * is given to them, then that would result in the thread - * deadlocking. So allowing a thread to take the rdlock - * when it already has one or more rdlocks avoids the - * deadlock. I hope the reader can follow that logic ;-) - */ - ; /* nothing needed */ - } else { /* give writers priority over readers */ - while (prwlock->blocked_writers || prwlock->state < 0) { + while (prwlock->blocked_writers || prwlock->state < 0 || + prwlock->state & WRITE_WAITERS) { if (abstime) ret = _pthread_cond_timedwait (&prwlock->read_signal, @@ -202,18 +218,15 @@ return (ret); } } - } - - curthread->rdlock_count++; - prwlock->state++; /* indicate we are locked for reading */ - /* - * Something is really wrong if this call fails. Returning - * error won't do because we've already obtained the read - * lock. Decrementing 'state' is no good because we probably - * don't have the monitor lock. - */ - _pthread_mutex_unlock(&prwlock->lock); + /* + * Something is really wrong if this call fails. Returning + * error won't do because we've already obtained the read + * lock. Decrementing 'state' is no good because we probably + * don't have the monitor lock. + */ + _pthread_mutex_unlock(&prwlock->lock); + } return (ret); } @@ -236,6 +249,7 @@ { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; + int state; int ret; if (rwlock == NULL) @@ -250,30 +264,17 @@ prwlock = *rwlock; } + do { + state = prwlock->state; + if (READ_COUNT(state) == MAX_READ_LOCKS) + return (EAGAIN); + if (state < 0) + return (EBUSY); + if ((state & WRITE_WAITERS) && curthread->rdlock_count == 0) + return (EBUSY); + } while (atomic_cmpset_acq_int((volatile int *)&prwlock->state, state, state + 1) == 0); - /* grab the monitor lock */ - if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) - return (ret); - - if (prwlock->state == MAX_READ_LOCKS) - ret = EAGAIN; - else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { - /* see comment for pthread_rwlock_rdlock() */ - curthread->rdlock_count++; - prwlock->state++; - } - /* give writers priority over readers */ - else if (prwlock->blocked_writers || prwlock->state < 0) - ret = EBUSY; - else { - curthread->rdlock_count++; - prwlock->state++; /* indicate we are locked for reading */ - } - - /* see the comment on this in pthread_rwlock_rdlock */ - _pthread_mutex_unlock(&prwlock->lock); - - return (ret); + return (0); } int @@ -281,6 +282,7 @@ { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; + int state; int ret; if (rwlock == NULL) @@ -295,19 +297,9 @@ prwlock = *rwlock; } - - /* grab the monitor lock */ - if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) - return (ret); - - if (prwlock->state != 0) + ret = !atomic_cmpset_acq_int((volatile int *)&prwlock->lock, 0, WRITE_OWNER); + if (ret) ret = EBUSY; - else - /* indicate we are locked for writing */ - prwlock->state = -1; - - /* see the comment on this in pthread_rwlock_rdlock */ - _pthread_mutex_unlock(&prwlock->lock); return (ret); } @@ -327,17 +319,21 @@ if (prwlock == NULL) return (EINVAL); + if (prwlock->state > 0) { + curthread->rdlock_count--; + ret = atomic_fetchadd_int((volatile int *)&prwlock->state, -1); + if (READ_COUNT(ret) != 1) + return (0); + } /* grab the monitor lock */ if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) return (ret); if (prwlock->state > 0) { - curthread->rdlock_count--; - prwlock->state--; - if (prwlock->state == 0 && prwlock->blocked_writers) + if (prwlock->state == WRITE_WAITERS) ret = _pthread_cond_signal(&prwlock->write_signal); } else if (prwlock->state < 0) { - prwlock->state = 0; + atomic_store_rel_int((volatile int *)&prwlock->state, 0); if (prwlock->blocked_writers) ret = _pthread_cond_signal(&prwlock->write_signal); @@ -372,11 +368,24 @@ prwlock = *rwlock; } - /* grab the monitor lock */ - if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) - return (ret); + while (atomic_cmpset_acq_int(&prwlock->state, 0, WRITE_OWNER) == 0) { + int state; - while (prwlock->state != 0) { + /* grab the monitor lock */ + if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) + return (ret); + state = prwlock->state; + if (state == 0) { + _pthread_mutex_unlock(&prwlock->lock); + continue; + } + if (state > 0 && (state & WRITE_WAITERS) == 0) { + if (atomic_cmpset_acq_int((volatile int *)&prwlock->state, + state, state | WRITE_WAITERS) == 0) { + _pthread_mutex_unlock(&prwlock->lock); + continue; + } + } prwlock->blocked_writers++; if (abstime != NULL) @@ -392,13 +401,10 @@ } prwlock->blocked_writers--; - } - /* indicate we are locked for writing */ - prwlock->state = -1; - - /* see the comment on this in pthread_rwlock_rdlock */ - _pthread_mutex_unlock(&prwlock->lock); + /* see the comment on this in pthread_rwlock_rdlock */ + _pthread_mutex_unlock(&prwlock->lock); + } return (ret); }