From 03a8d066680b4582b9736fb2b565d602c99aa11a Mon Sep 17 00:00:00 2001 From: Raja Appuswamy Date: Tue, 29 Nov 2011 14:03:49 +0100 Subject: [PATCH] adding rwlock and event support to mthread --- common/include/minix/mthread.h | 25 +++++ lib/libmthread/Makefile | 2 + lib/libmthread/event.c | 99 +++++++++++++++++++ lib/libmthread/rwlock.c | 130 ++++++++++++++++++++++++ test/test59.c | 175 +++++++++++++++++++++++++++++++++ 5 files changed, 431 insertions(+) create mode 100644 lib/libmthread/event.c create mode 100644 lib/libmthread/rwlock.c diff --git a/common/include/minix/mthread.h b/common/include/minix/mthread.h index dfefbda88..38f335868 100644 --- a/common/include/minix/mthread.h +++ b/common/include/minix/mthread.h @@ -56,6 +56,18 @@ struct __mthread_attr { }; typedef struct __mthread_attr *mthread_attr_t; +typedef struct { + mthread_mutex_t mutex; + mthread_cond_t cond; +} mthread_event_t; + +typedef struct { + unsigned int readers; + mthread_thread_t writer; + mthread_mutex_t queue; + mthread_event_t drain; +} mthread_rwlock_t; + #define MTHREAD_CREATE_JOINABLE 001 #define MTHREAD_CREATE_DETACHED 002 #define MTHREAD_ONCE_INIT 0 @@ -122,6 +134,19 @@ _PROTOTYPE( int mthread_mutex_lock, (mthread_mutex_t *mutex) ); _PROTOTYPE( int mthread_mutex_trylock, (mthread_mutex_t *mutex) ); _PROTOTYPE( int mthread_mutex_unlock, (mthread_mutex_t *mutex) ); +/* event.c */ +_PROTOTYPE( int mthread_event_destroy, (mthread_event_t *event) ); +_PROTOTYPE( int mthread_event_init, (mthread_event_t *event) ); +_PROTOTYPE( int mthread_event_wait, (mthread_event_t *event) ); +_PROTOTYPE( int mthread_event_fire, (mthread_event_t *event) ); + +/* rwlock.c */ +_PROTOTYPE( int mthread_rwlock_destroy, (mthread_rwlock_t *rwlock) ); +_PROTOTYPE( int mthread_rwlock_init, (mthread_rwlock_t *rwlock) ); +_PROTOTYPE( int mthread_rwlock_rdlock, (mthread_rwlock_t *rwlock) ); +_PROTOTYPE( int mthread_rwlock_wrlock, (mthread_rwlock_t *rwlock) ); +_PROTOTYPE( int mthread_rwlock_unlock, (mthread_rwlock_t *rwlock) ); + /* schedule.c */ _PROTOTYPE( void mthread_init, (void) ); _PROTOTYPE( int mthread_yield, (void) ); diff --git a/lib/libmthread/Makefile b/lib/libmthread/Makefile index a35cc5e64..6c61a76e9 100644 --- a/lib/libmthread/Makefile +++ b/lib/libmthread/Makefile @@ -8,6 +8,8 @@ SRCS= \ allocate.c \ attribute.c \ mutex.c \ + event.c \ + rwlock.c \ misc.c \ queue.c \ condition.c \ diff --git a/lib/libmthread/event.c b/lib/libmthread/event.c new file mode 100644 index 000000000..12918006a --- /dev/null +++ b/lib/libmthread/event.c @@ -0,0 +1,99 @@ +#include +#include "global.h" + +/*===========================================================================* + * mthread_event_init * + *===========================================================================*/ +PUBLIC int mthread_event_init(event) +mthread_event_t *event; /* The event to be initialized */ +{ +/* Initialize an event object. + */ + int r; + + if (!event) + return EINVAL; + + r = mthread_mutex_init(&event->mutex, NULL); + if (r != 0) + return r; + + r = mthread_cond_init(&event->cond, NULL); + if (r != 0) + mthread_mutex_destroy(&event->mutex); + + return r; +} + + +/*===========================================================================* + * mthread_event_destroy * + *===========================================================================*/ +PUBLIC int mthread_event_destroy(event) +mthread_event_t *event; /* The event to be destroyed */ +{ +/* Destroy an event object. + */ + int r; + + if (!event) + return EINVAL; + + r = mthread_cond_destroy(&event->cond); + if (r != 0) + return r; + + return mthread_mutex_destroy(&event->mutex); +} + +/*===========================================================================* + * mthread_event_wait * + *===========================================================================*/ +PUBLIC int mthread_event_wait(event) +mthread_event_t *event; /* The event to be waited on */ +{ +/* Wait for an event, blocking the current thread in the process. + */ + int r; + + if (!event) + return EINVAL; + + r = mthread_mutex_lock(&event->mutex); + if (r != 0) + return r; + + r = mthread_cond_wait(&event->cond, &event->mutex); + if (r != 0) { + mthread_mutex_unlock(&event->mutex); + return r; + } + + return mthread_mutex_unlock(&event->mutex); +} + +/*===========================================================================* + * mthread_event_fire * + *===========================================================================*/ +PUBLIC int mthread_event_fire(event) +mthread_event_t *event; /* The event to be fired */ +{ +/* Fire an event, waking up any thread blocked on it. +*/ + int r; + + if (!event) + return EINVAL; + + r = mthread_mutex_lock(&event->mutex); + if (r != 0) + return r; + + r = mthread_cond_signal(&event->cond); + if (r != 0) { + mthread_mutex_unlock(&event->mutex); + return r; + } + + return mthread_mutex_unlock(&event->mutex); +} diff --git a/lib/libmthread/rwlock.c b/lib/libmthread/rwlock.c new file mode 100644 index 000000000..e223828f7 --- /dev/null +++ b/lib/libmthread/rwlock.c @@ -0,0 +1,130 @@ +#include +#include "global.h" + +/*===========================================================================* + * mthread_rwlock_init * + *===========================================================================*/ +PUBLIC int mthread_rwlock_init(rwlock) +mthread_rwlock_t *rwlock; /* The rwlock to be initialized */ +{ + /* Initialize a readers/writer lock. */ + int r; + + if (!rwlock) + return EINVAL; + + rwlock->writer = NO_THREAD; + rwlock->readers = 0; + + r = mthread_mutex_init(&rwlock->queue, NULL); + if (r != 0) + return r; + + r = mthread_event_init(&rwlock->drain); + if (r != 0) + mthread_mutex_destroy(&rwlock->queue); + + return r; +} + +/*===========================================================================* + * mthread_rwlock_destroy * + *===========================================================================*/ +PUBLIC int mthread_rwlock_destroy(rwlock) +mthread_rwlock_t *rwlock; /* The rwlock to be destroyed */ +{ + /* Destroy a readers/writer lock. */ + int r; + + if (!rwlock) + return EINVAL; + + assert(rwlock->writer == NO_THREAD); + assert(rwlock->readers == 0); + + r = mthread_event_destroy(&rwlock->drain); + if (r != 0) + return r; + + return mthread_mutex_destroy(&rwlock->queue); +} + +/*===========================================================================* + * mthread_rwlock_rdlock * + *===========================================================================*/ +PUBLIC int mthread_rwlock_rdlock(rwlock) +mthread_rwlock_t *rwlock; /* The rwlock to be read locked */ +{ + /* Acquire a reader lock. */ + int r; + + if (!rwlock) + return EINVAL; + + r = mthread_mutex_lock(&rwlock->queue); + if (r != 0) + return r; + + r = mthread_mutex_unlock(&rwlock->queue); + if (r != 0) + return r; + + rwlock->readers++; + + return 0; +} + +/*===========================================================================* + * mthread_rwlock_wrlock * + *===========================================================================*/ +PUBLIC int mthread_rwlock_wrlock(rwlock) +mthread_rwlock_t *rwlock; /* The rwlock to be write locked */ +{ + /* Acquire a writer lock. */ + int r; + + if (!rwlock) + return EINVAL; + + r = mthread_mutex_lock(&rwlock->queue); + if (r != 0) + return r; + + rwlock->writer = current_thread; + + if (rwlock->readers > 0) + r = mthread_event_wait(&rwlock->drain); + + if (r == 0) + assert(rwlock->readers == 0); + + return r; +} + +/*===========================================================================* + * mthread_rwlock_unlock * + *===========================================================================*/ +PUBLIC int mthread_rwlock_unlock(rwlock) +mthread_rwlock_t *rwlock; /* The rwlock to be unlocked */ +{ + /* Release a lock. */ + int r; + + r = 0; + if (!rwlock) + return EINVAL; + + if (rwlock->writer == current_thread) { + rwlock->writer = NO_THREAD; + r = mthread_mutex_unlock(&rwlock->queue); + } else { + assert(rwlock->readers > 0); + + rwlock->readers--; + + if (rwlock->readers == 0 && rwlock->writer != NO_THREAD) + r = mthread_event_fire(&rwlock->drain); + } + + return r; +} diff --git a/test/test59.c b/test/test59.c index 57b3c771f..927dd8d94 100644 --- a/test/test59.c +++ b/test/test59.c @@ -11,6 +11,8 @@ #define once_t mthread_once_t #define attr_t mthread_attr_t #define key_t mthread_key_t +#define event_t mthread_event_t +#define rwlock_t mthread_rwlock_t #define MAX_ERROR 5 #include "common.c" @@ -26,6 +28,24 @@ PRIVATE once_t once; PRIVATE key_t key[MTHREAD_KEYS_MAX+1]; PRIVATE int values[4]; PRIVATE int first; +PRIVATE event_t event; +PRIVATE int event_a_step, event_b_step; +PRIVATE rwlock_t rwlock; +PRIVATE int rwlock_a_step, rwlock_b_step; + +#define VERIFY_RWLOCK(a, b, esub, eno) \ + GEN_VERIFY(rwlock_a_step, a, rwlock_b_step, b, esub, eno) + +#define VERIFY_EVENT(a, b, esub, eno) \ + GEN_VERIFY(event_a_step, a, event_b_step, b, esub, eno) + +#define GEN_VERIFY(acta, a, actb, b, esub, eno) do { \ + if (acta != a) { \ + printf("Expected %d %d, got: %d %d\n", \ + a, b, acta, actb); \ + err(esub, eno); \ + } else if (actb != b) err(esub, eno); \ + } while(0) #define VERIFY_MUTEX(a,b,c,esub,eno) do { \ if (mutex_a_step != a) { \ @@ -920,6 +940,157 @@ PRIVATE void test_keys(void) if (mthread_join(t[i], NULL) != 0) err(20, 28); } +/*===========================================================================* + * event_a * + *===========================================================================*/ +PRIVATE void *event_a(void *arg) +{ + VERIFY_EVENT(0, 0, 21, 1); + + if (mthread_event_wait(&event) != 0) err(21, 2); + + event_a_step = 1; + + if (mthread_event_fire(&event) != 0) err(21, 3); + + mthread_yield(); + + VERIFY_EVENT(1, 1, 21, 4); + + return(NULL); +} + +/*===========================================================================* + * event_b * + *===========================================================================*/ +PRIVATE void *event_b(void *arg) +{ + VERIFY_EVENT(0, 0, 22, 1); + + if (mthread_event_wait(&event) != 0) err(22, 2); + + VERIFY_EVENT(1, 0, 22, 3); + + event_b_step = 1; + + return(NULL); +} + +/*===========================================================================* + * test_event * + *===========================================================================*/ +PRIVATE void test_event(void) +{ + thread_t t[2]; + int i; + + if (mthread_event_init(&event) != 0) err(23, 1); + + /* Try with faulty memory locations */ + if (mthread_event_wait(NULL) == 0) err(23, 2); + if (mthread_event_fire(NULL) == 0) err(23, 3); + + if (mthread_create(&t[0], NULL, event_a, NULL) != 0) err(23, 4); + if (mthread_create(&t[1], NULL, event_b, NULL) != 0) err(23, 5); + + mthread_yield_all(); + + VERIFY_EVENT(0, 0, 23, 6); + + if (mthread_event_fire(&event) != 0) err(23, 7); + + mthread_yield_all(); + + for (i = 0; i < 2; i++) + if (mthread_join(t[i], NULL) != 0) err(23, 8); + + if (mthread_event_destroy(&event) != 0) err(23, 9); +} + +/*===========================================================================* + * rwlock_a * + *===========================================================================*/ +PRIVATE void *rwlock_a(void *arg) +{ + /* acquire read lock */ + VERIFY_RWLOCK(0, 0, 24, 1); + if (mthread_rwlock_rdlock(&rwlock) != 0) err(24, 2); + rwlock_a_step = 1; + mthread_yield(); + + /* release read lock */ + VERIFY_RWLOCK(1, 1, 24, 3); + if (mthread_rwlock_unlock(&rwlock) != 0) err(24, 4); + rwlock_a_step = 2; + + /* get write lock */ + if (mthread_rwlock_wrlock(&rwlock) != 0) err(24, 5); + rwlock_a_step = 3; + VERIFY_RWLOCK(3, 2, 24, 6); + + /* release write lock */ + if (mthread_rwlock_unlock(&rwlock) != 0) err(24, 7); + mthread_yield(); + + VERIFY_RWLOCK(3, 3, 24, 8); + + return(NULL); +} + +/*===========================================================================* + * rwlock_b * + *===========================================================================*/ +PRIVATE void *rwlock_b(void *arg) +{ + /* Step 1: acquire the read lock */ + VERIFY_RWLOCK(1, 0, 25, 1); + if (mthread_rwlock_rdlock(&rwlock) != 0) err(25, 2); + rwlock_b_step = 1; + mthread_yield(); + + /* We return back with first thread blocked on wrlock */ + VERIFY_RWLOCK(2, 1, 25, 3); + rwlock_b_step = 2; + + /* Release read lock and acquire write lock */ + if (mthread_rwlock_unlock(&rwlock) != 0) err(25, 4); + if (mthread_rwlock_wrlock(&rwlock) != 0) err(25, 5); + rwlock_b_step = 3; + + VERIFY_RWLOCK(3, 3, 25, 6); + if (mthread_rwlock_unlock(&rwlock) != 0) err(25, 6); + + return(NULL); +} + +/*===========================================================================* + * test_rwlock * + *===========================================================================*/ +PRIVATE void test_rwlock(void) +{ + thread_t t[2]; + int i; + + if (mthread_rwlock_init(&rwlock) != 0) err(26, 1); + + /* Try with faulty memory locations */ + if (mthread_rwlock_rdlock(NULL) == 0) err(26, 2); + if (mthread_rwlock_wrlock(NULL) == 0) err(26, 3); + if (mthread_rwlock_unlock(NULL) == 0) err(26, 4); + + /* Create the threads and start testing */ + if (mthread_create(&t[0], NULL, rwlock_a, NULL) != 0) err(26, 5); + if (mthread_create(&t[1], NULL, rwlock_b, NULL) != 0) err(26, 6); + + mthread_yield_all(); + + for (i = 0; i < 2; i++) + if (mthread_join(t[i], NULL) != 0) err(26, 7); + + if (mthread_rwlock_destroy(&rwlock) != 0) err(26, 8); +} + + /*===========================================================================* * main * *===========================================================================*/ @@ -928,12 +1099,16 @@ int main(void) errct = 0; th_a = th_b = th_c = th_d = th_e = th_f = th_g = th_h = 0; mutex_a_step = mutex_b_step = mutex_c_step = 0; + event_a_step = event_b_step = 0; + rwlock_a_step = rwlock_b_step = 0; once = MTHREAD_ONCE_INIT; start(59); mthread_init(); test_scheduling(); test_mutex(); + test_event(); + test_rwlock(); test_condition(); test_attributes(); test_keys();