diff --git a/bin/tests/rwlock_test.c b/bin/tests/rwlock_test.c new file mode 100644 index 0000000000..91fb784b74 --- /dev/null +++ b/bin/tests/rwlock_test.c @@ -0,0 +1,100 @@ + +#include "attribute.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +isc_rwlock_t lock; + +static void * +run1(void *arg) { + char *message = arg; + + INSIST(isc_rwlock_lock(&lock, isc_rwlocktype_read) == ISC_R_SUCCESS); + printf("%s got READ lock\n", message); + sleep(1); + printf("%s giving up READ lock\n", message); + INSIST(isc_rwlock_unlock(&lock, isc_rwlocktype_read) == + ISC_R_SUCCESS); + INSIST(isc_rwlock_lock(&lock, isc_rwlocktype_read) == ISC_R_SUCCESS); + printf("%s got READ lock\n", message); + sleep(1); + printf("%s giving up READ lock\n", message); + INSIST(isc_rwlock_unlock(&lock, isc_rwlocktype_read) == + ISC_R_SUCCESS); + INSIST(isc_rwlock_lock(&lock, isc_rwlocktype_write) == ISC_R_SUCCESS); + printf("%s got WRITE lock\n", message); + sleep(1); + printf("%s giving up WRITE lock\n", message); + INSIST(isc_rwlock_unlock(&lock, isc_rwlocktype_write) == + ISC_R_SUCCESS); + return (NULL); +} + +static void * +run2(void *arg) { + char *message = arg; + + INSIST(isc_rwlock_lock(&lock, isc_rwlocktype_write) == ISC_R_SUCCESS); + printf("%s got WRITE lock\n", message); + sleep(1); + printf("%s giving up WRITE lock\n", message); + INSIST(isc_rwlock_unlock(&lock, isc_rwlocktype_write) == + ISC_R_SUCCESS); + INSIST(isc_rwlock_lock(&lock, isc_rwlocktype_write) == ISC_R_SUCCESS); + printf("%s got WRITE lock\n", message); + sleep(1); + printf("%s giving up WRITE lock\n", message); + INSIST(isc_rwlock_unlock(&lock, isc_rwlocktype_write) == + ISC_R_SUCCESS); + INSIST(isc_rwlock_lock(&lock, isc_rwlocktype_read) == ISC_R_SUCCESS); + printf("%s got READ lock\n", message); + sleep(1); + printf("%s giving up READ lock\n", message); + INSIST(isc_rwlock_unlock(&lock, isc_rwlocktype_read) == + ISC_R_SUCCESS); + return (NULL); +} + +void +main(int argc, char *argv[]) { + unsigned int nworkers; + unsigned int i; + isc_thread_t workers[24]; + char name[100]; + void *dupname; + + if (argc > 1) + nworkers = atoi(argv[1]); + else + nworkers = 2; + if (nworkers > 24) + nworkers = 24; + printf("%d workers\n", nworkers); + + INSIST(isc_rwlock_init(&lock) == ISC_R_SUCCESS); + + for (i = 0; i < nworkers; i++) { + sprintf(name, "%02u", i); + dupname = strdup(name); + INSIST(dupname != NULL); + if (i != 0 && i % 3 == 0) + INSIST(isc_thread_create(run2, dupname, &workers[i]) == + ISC_R_SUCCESS); + else + INSIST(isc_thread_create(run1, dupname, &workers[i]) == + ISC_R_SUCCESS); + } + + for (i = 0; i < nworkers; i++) + (void)isc_thread_join(workers[i], NULL); + + isc_rwlock_destroy(&lock); +} diff --git a/lib/isc/include/isc/rwlock.h b/lib/isc/include/isc/rwlock.h new file mode 100644 index 0000000000..7f3b009cb3 --- /dev/null +++ b/lib/isc/include/isc/rwlock.h @@ -0,0 +1,38 @@ + +#ifndef ISC_RWLOCK_H +#define ISC_RWLOCK_H 1 + +#include +#include +#include + +typedef enum { + isc_rwlocktype_read = 0, + isc_rwlocktype_write +} isc_rwlocktype_t; + +typedef struct isc_rwlock { + isc_mutex_t lock; + isc_condition_t readable; + isc_condition_t writeable; + isc_rwlocktype_t type; + unsigned int active; + unsigned int granted; + unsigned int readers_waiting; + unsigned int writers_waiting; + unsigned int read_quota; +} isc_rwlock_t; + +isc_result_t +isc_rwlock_init(isc_rwlock_t *rwl); + +isc_result_t +isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type); + +isc_result_t +isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type); + +void +isc_rwlock_destroy(isc_rwlock_t *rwl); + +#endif /* ISC_RWLOCK_H */ diff --git a/lib/isc/rwlock.c b/lib/isc/rwlock.c new file mode 100644 index 0000000000..f8fb10be0d --- /dev/null +++ b/lib/isc/rwlock.c @@ -0,0 +1,183 @@ + +#include +#include +#include +#include + +#define LOCK(lp) \ + INSIST(isc_mutex_lock((lp)) == ISC_R_SUCCESS); +#define UNLOCK(lp) \ + INSIST(isc_mutex_unlock((lp)) == ISC_R_SUCCESS); +#define BROADCAST(cvp) \ + INSIST(isc_condition_broadcast((cvp)) == ISC_R_SUCCESS); +#define SIGNAL(cvp) \ + INSIST(isc_condition_signal((cvp)) == ISC_R_SUCCESS); +#define WAIT(cvp, lp) \ + INSIST(isc_condition_wait((cvp), (lp)) == ISC_R_SUCCESS); + +#include + +static void +print_lock(char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) { + printf("%s(%s): ", operation, + (type == isc_rwlocktype_read ? "read" : "write")); + printf("%s, %u active", + (rwl->type == isc_rwlocktype_read ? "reading" : "writing"), + rwl->active); + if (rwl->type == isc_rwlocktype_read) + printf(", %u granted", rwl->granted); + printf(", %u rwaiting, %u wwaiting\n", + rwl->readers_waiting, + rwl->writers_waiting); +} + +isc_result_t +isc_rwlock_init(isc_rwlock_t *rwl) { + isc_result_t result; + + REQUIRE(rwl != NULL); + + rwl->type = isc_rwlocktype_read; + rwl->active = 0; + rwl->granted = 0; + rwl->readers_waiting = 0; + rwl->writers_waiting = 0; + rwl->read_quota = 5; /* XXX */ + result = isc_mutex_init(&rwl->lock); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + result = isc_condition_init(&rwl->readable); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init(readable) failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + result = isc_condition_init(&rwl->writeable); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init(writeable) failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + isc_boolean_t skip = ISC_FALSE; + isc_boolean_t done = ISC_FALSE; + + LOCK(&rwl->lock); + +#ifdef DEBUG + print_lock("prelock", rwl, type); +#endif + + if (type == isc_rwlocktype_read) { + if (rwl->readers_waiting != 0) + skip = ISC_TRUE; + while (!done) { + if (!skip && + ((rwl->active == 0 || + (rwl->type == isc_rwlocktype_read && + rwl->granted < rwl->read_quota)))) { + rwl->type = isc_rwlocktype_read; + rwl->active++; + rwl->granted++; + done = ISC_TRUE; + } else { + skip = ISC_FALSE; + rwl->readers_waiting++; + WAIT(&rwl->readable, &rwl->lock); + rwl->readers_waiting--; + } + } + } else { + if (rwl->writers_waiting != 0) + skip = ISC_TRUE; + while (!done) { + if (!skip && rwl->active == 0) { + rwl->type = isc_rwlocktype_write; + rwl->active = 1; + done = ISC_TRUE; + } else { + skip = ISC_FALSE; + rwl->writers_waiting++; + WAIT(&rwl->writeable, &rwl->lock); + rwl->writers_waiting--; + } + } + } + +#ifdef DEBUG + print_lock("postlock", rwl, type); +#endif + + UNLOCK(&rwl->lock); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + LOCK(&rwl->lock); + REQUIRE(rwl->type == type); + +#ifdef DEBUG + print_lock("preunlock", rwl, type); +#endif + + rwl->active--; + if (rwl->active == 0) { + rwl->granted = 0; + if (rwl->type == isc_rwlocktype_read) { + if (rwl->writers_waiting > 0) { + rwl->type = isc_rwlocktype_write; + SIGNAL(&rwl->writeable); + } else if (rwl->readers_waiting > 0) { + BROADCAST(&rwl->readable); + } + } else { + if (rwl->readers_waiting > 0) { + rwl->type = isc_rwlocktype_read; + BROADCAST(&rwl->readable); + } else if (rwl->writers_waiting > 0) { + SIGNAL(&rwl->writeable); + } + } + } else { + if (rwl->type == isc_rwlocktype_read && + rwl->writers_waiting == 0 && + rwl->readers_waiting > 0) { + INSIST(rwl->granted > 0); + rwl->granted--; + SIGNAL(&rwl->readable); + } + } + +#ifdef DEBUG + print_lock("postunlock", rwl, type); +#endif + + UNLOCK(&rwl->lock); + + return (ISC_R_SUCCESS); +} + +void +isc_rwlock_destroy(isc_rwlock_t *rwl) { + LOCK(&rwl->lock); + REQUIRE(rwl->active == 0 && + rwl->readers_waiting == 0 && + rwl->writers_waiting == 0); + UNLOCK(&rwl->lock); + (void)isc_condition_destroy(&rwl->readable); + (void)isc_condition_destroy(&rwl->writeable); + (void)isc_mutex_destroy(&rwl->lock); +}