]> Zhao Yanbai Git Server - minix.git/commitdiff
adding rwlock and event support to mthread
authorRaja Appuswamy <raja@minix3.org>
Tue, 29 Nov 2011 13:03:49 +0000 (14:03 +0100)
committerBen Gras <ben@minix3.org>
Tue, 29 Nov 2011 13:35:22 +0000 (14:35 +0100)
common/include/minix/mthread.h
lib/libmthread/Makefile
lib/libmthread/event.c [new file with mode: 0644]
lib/libmthread/rwlock.c [new file with mode: 0644]
test/test59.c

index dfefbda88659f70640e5c91d4f0459be1f8a8050..38f335868c394aa7c6cd5f58d8f569a3f74513b1 100644 (file)
@@ -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)                                  );
index a35cc5e64c8482ee0c75a0555e01e22800eab080..6c61a76e9b1ee3477d46f273c451264bb8ca9e31 100644 (file)
@@ -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 (file)
index 0000000..1291800
--- /dev/null
@@ -0,0 +1,99 @@
+#include <minix/mthread.h>
+#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 (file)
index 0000000..e223828
--- /dev/null
@@ -0,0 +1,130 @@
+#include <minix/mthread.h>
+#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;
+}
index 57b3c771ff3523c3db9f583d48a1452de9efc7f1..927dd8d94d21e7574f494a49df49c2940118a707 100644 (file)
@@ -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();