add
This commit is contained in:
100
bin/tests/rwlock_test.c
Normal file
100
bin/tests/rwlock_test.c
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
#include "attribute.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <isc/assertions.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/rwlock.h>
|
||||
|
||||
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);
|
||||
}
|
||||
38
lib/isc/include/isc/rwlock.h
Normal file
38
lib/isc/include/isc/rwlock.h
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
#ifndef ISC_RWLOCK_H
|
||||
#define ISC_RWLOCK_H 1
|
||||
|
||||
#include <isc/result.h>
|
||||
#include <isc/mutex.h>
|
||||
#include <isc/condition.h>
|
||||
|
||||
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 */
|
||||
183
lib/isc/rwlock.c
Normal file
183
lib/isc/rwlock.c
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
#include <isc/assertions.h>
|
||||
#include <isc/unexpect.h>
|
||||
#include <isc/boolean.h>
|
||||
#include <isc/rwlock.h>
|
||||
|
||||
#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 <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user