initial import
from http://www.informit.com/store/programming-with-posix-threads-9780201633924
This commit is contained in:
276
rwlock.c
Normal file
276
rwlock.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* rwlock.c
|
||||
*
|
||||
* This file implements the "read-write lock" synchronization
|
||||
* construct.
|
||||
*
|
||||
* A read-write lock allows a thread to lock shared data either
|
||||
* for shared read access or exclusive write access.
|
||||
*
|
||||
* The rwl_init() and rwl_destroy() functions, respectively,
|
||||
* allow you to initialize/create and destroy/free the
|
||||
* read-write lock.
|
||||
*
|
||||
* The rwl_readlock() function locks a read-write lock for
|
||||
* shared read access, and rwl_readunlock() releases the
|
||||
* lock. rwl_readtrylock() attempts to lock a read-write lock
|
||||
* for read access, and returns EBUSY instead of blocking.
|
||||
*
|
||||
* The rwl_writelock() function locks a read-write lock for
|
||||
* exclusive write access, and rwl_writeunlock() releases the
|
||||
* lock. rwl_writetrylock() attempts to lock a read-write lock
|
||||
* for write access, and returns EBUSY instead of blocking.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
#include "rwlock.h"
|
||||
|
||||
/*
|
||||
* Initialize a read-write lock
|
||||
*/
|
||||
int rwl_init (rwlock_t *rwl)
|
||||
{
|
||||
int status;
|
||||
|
||||
rwl->r_active = 0;
|
||||
rwl->r_wait = rwl->w_wait = 0;
|
||||
rwl->w_active = 0;
|
||||
status = pthread_mutex_init (&rwl->mutex, NULL);
|
||||
if (status != 0)
|
||||
return status;
|
||||
status = pthread_cond_init (&rwl->read, NULL);
|
||||
if (status != 0) {
|
||||
/* if unable to create read CV, destroy mutex */
|
||||
pthread_mutex_destroy (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
status = pthread_cond_init (&rwl->write, NULL);
|
||||
if (status != 0) {
|
||||
/* if unable to create write CV, destroy read CV and mutex */
|
||||
pthread_cond_destroy (&rwl->read);
|
||||
pthread_mutex_destroy (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
rwl->valid = RWLOCK_VALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a read-write lock
|
||||
*/
|
||||
int rwl_destroy (rwlock_t *rwl)
|
||||
{
|
||||
int status, status1, status2;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* Check whether any threads own the lock; report "BUSY" if
|
||||
* so.
|
||||
*/
|
||||
if (rwl->r_active > 0 || rwl->w_active) {
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether any threads are known to be waiting; report
|
||||
* EBUSY if so.
|
||||
*/
|
||||
if (rwl->r_wait != 0 || rwl->w_wait != 0) {
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
rwl->valid = 0;
|
||||
status = pthread_mutex_unlock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
status = pthread_mutex_destroy (&rwl->mutex);
|
||||
status1 = pthread_cond_destroy (&rwl->read);
|
||||
status2 = pthread_cond_destroy (&rwl->write);
|
||||
return (status == 0 ? status : (status1 == 0 ? status1 : status2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle cleanup when the read lock condition variable
|
||||
* wait is cancelled.
|
||||
*
|
||||
* Simply record that the thread is no longer waiting,
|
||||
* and unlock the mutex.
|
||||
*/
|
||||
static void rwl_readcleanup (void *arg)
|
||||
{
|
||||
rwlock_t *rwl = (rwlock_t *)arg;
|
||||
|
||||
rwl->r_wait--;
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock a read-write lock for read access.
|
||||
*/
|
||||
int rwl_readlock (rwlock_t *rwl)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
if (rwl->w_active) {
|
||||
rwl->r_wait++;
|
||||
pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
|
||||
while (rwl->w_active) {
|
||||
status = pthread_cond_wait (&rwl->read, &rwl->mutex);
|
||||
if (status != 0)
|
||||
break;
|
||||
}
|
||||
pthread_cleanup_pop (0);
|
||||
rwl->r_wait--;
|
||||
}
|
||||
if (status == 0)
|
||||
rwl->r_active++;
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to lock a read-write lock for read access (don't
|
||||
* block if unavailable).
|
||||
*/
|
||||
int rwl_readtrylock (rwlock_t *rwl)
|
||||
{
|
||||
int status, status2;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
if (rwl->w_active)
|
||||
status = EBUSY;
|
||||
else
|
||||
rwl->r_active++;
|
||||
status2 = pthread_mutex_unlock (&rwl->mutex);
|
||||
return (status2 != 0 ? status2 : status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock a read-write lock from read access.
|
||||
*/
|
||||
int rwl_readunlock (rwlock_t *rwl)
|
||||
{
|
||||
int status, status2;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
rwl->r_active--;
|
||||
if (rwl->r_active == 0 && rwl->w_wait > 0)
|
||||
status = pthread_cond_signal (&rwl->write);
|
||||
status2 = pthread_mutex_unlock (&rwl->mutex);
|
||||
return (status2 == 0 ? status : status2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle cleanup when the write lock condition variable
|
||||
* wait is cancelled.
|
||||
*
|
||||
* Simply record that the thread is no longer waiting,
|
||||
* and unlock the mutex.
|
||||
*/
|
||||
static void rwl_writecleanup (void *arg)
|
||||
{
|
||||
rwlock_t *rwl = (rwlock_t *)arg;
|
||||
|
||||
rwl->w_wait--;
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock a read-write lock for write access.
|
||||
*/
|
||||
int rwl_writelock (rwlock_t *rwl)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
if (rwl->w_active || rwl->r_active > 0) {
|
||||
rwl->w_wait++;
|
||||
pthread_cleanup_push (rwl_writecleanup, (void*)rwl);
|
||||
while (rwl->w_active || rwl->r_active > 0) {
|
||||
status = pthread_cond_wait (&rwl->write, &rwl->mutex);
|
||||
if (status != 0)
|
||||
break;
|
||||
}
|
||||
pthread_cleanup_pop (0);
|
||||
rwl->w_wait--;
|
||||
}
|
||||
if (status == 0)
|
||||
rwl->w_active = 1;
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to lock a read-write lock for write access. Don't
|
||||
* block if unavailable.
|
||||
*/
|
||||
int rwl_writetrylock (rwlock_t *rwl)
|
||||
{
|
||||
int status, status2;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
if (rwl->w_active || rwl->r_active > 0)
|
||||
status = EBUSY;
|
||||
else
|
||||
rwl->w_active = 1;
|
||||
status2 = pthread_mutex_unlock (&rwl->mutex);
|
||||
return (status != 0 ? status : status2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock a read-write lock from write access.
|
||||
*/
|
||||
int rwl_writeunlock (rwlock_t *rwl)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (rwl->valid != RWLOCK_VALID)
|
||||
return EINVAL;
|
||||
status = pthread_mutex_lock (&rwl->mutex);
|
||||
if (status != 0)
|
||||
return status;
|
||||
rwl->w_active = 0;
|
||||
if (rwl->r_wait > 0) {
|
||||
status = pthread_cond_broadcast (&rwl->read);
|
||||
if (status != 0) {
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
} else if (rwl->w_wait > 0) {
|
||||
status = pthread_cond_signal (&rwl->write);
|
||||
if (status != 0) {
|
||||
pthread_mutex_unlock (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
status = pthread_mutex_unlock (&rwl->mutex);
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user