Implemented a read-write lock using only mutex_t and condvar_t (in case the pthread_r...
authorTobias Brunner <tobias@strongswan.org>
Tue, 8 Dec 2009 13:06:11 +0000 (14:06 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 23 Dec 2009 16:01:30 +0000 (17:01 +0100)
configure.in
src/libstrongswan/threading/rwlock.c
src/libstrongswan/threading/rwlock.h

index 2c2ffaf..cb3910a 100644 (file)
@@ -297,7 +297,8 @@ AC_TRY_RUN(
                [AC_DEFINE([HAVE_CONDATTR_CLOCK_MONOTONIC])]
        )]
 )
-
+dnl check if native rwlocks are available
+AC_CHECK_FUNCS(pthread_rwlock_init)
 LIBS=$saved_LIBS
 
 AC_CHECK_FUNCS(prctl)
index a11369d..bf59f50 100644 (file)
@@ -24,6 +24,8 @@
 #include "rwlock.h"
 #include "lock_profiler.h"
 
+#ifdef HAVE_PTHREAD_RWLOCK_INIT
+
 /**
  * Implementation of rwlock_t.read_lock
  */
@@ -114,3 +116,151 @@ rwlock_t *rwlock_create(rwlock_type_t type)
        }
 }
 
+#else /* HAVE_PTHREAD_RWLOCK_INIT */
+
+/**
+ * This implementation of the rwlock_t interface uses mutex_t and condvar_t
+ * primitives, if the pthread_rwlock_* group of functions is not available.
+ *
+ * The following constraints are enforced:
+ *   - Multiple readers can hold the lock at the same time.
+ *   - Only a single writer can hold the lock at any given time.
+ *   - A writer must block until all readers have released the lock before
+ *     obtaining the lock exclusively.
+ *   - Readers that arrive while a writer is waiting to acquire the lock will
+ *     block until after the writer has obtained and released the lock.
+ * These constraints allow for read sharing, prevent write sharing, prevent
+ * read-write sharing and prevent starvation of writers by a steady stream
+ * of incoming readers. Reader starvation is not prevented (this could happen
+ * if there are more writers than readers).
+ *
+ * The implementation does not support recursive locking and readers must not
+ * acquire the lock exclusively at the same time and vice-versa (this is not
+ * checked or enforced so behave yourself to prevent deadlocks).
+ */
+
+/**
+ * Implementation of rwlock_t.read_lock
+ */
+static void read_lock(private_rwlock_t *this)
+{
+       profiler_start(&this->profile);
+       this->mutex->lock(this->mutex);
+       while (this->writer || this->waiting_writers)
+       {
+               this->readers->wait(this->readers, this->mutex);
+       }
+       this->reader_count++;
+       profiler_end(&this->profile);
+       this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Implementation of rwlock_t.write_lock
+ */
+static void write_lock(private_rwlock_t *this)
+{
+       profiler_start(&this->profile);
+       this->mutex->lock(this->mutex);
+       this->waiting_writers++;
+       while (this->writer || this->reader_count)
+       {
+               this->writers->wait(this->writers, this->mutex);
+       }
+       this->waiting_writers--;
+       this->writer = pthread_self();
+       profiler_end(&this->profile);
+       this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Implementation of rwlock_t.try_write_lock
+ */
+static bool try_write_lock(private_rwlock_t *this)
+{
+       bool res = FALSE;
+       this->mutex->lock(this->mutex);
+       if (!this->writer && !this->reader_count)
+       {
+               res = TRUE;
+               this->writer = pthread_self();
+       }
+       this->mutex->unlock(this->mutex);
+       return res;
+}
+
+/**
+ * Implementation of rwlock_t.unlock
+ */
+static void rw_unlock(private_rwlock_t *this)
+{
+       this->mutex->lock(this->mutex);
+       if (this->writer == pthread_self())
+       {
+               this->writer = 0;
+               if (this->waiting_writers)
+               {
+                       this->writers->signal(this->writers);
+               }
+               else
+               {
+                       this->readers->broadcast(this->readers);
+               }
+       }
+       else
+       {
+               this->reader_count--;
+               if (!this->reader_count)
+               {
+                       this->writers->signal(this->writers);
+               }
+       }
+       this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Implementation of rwlock_t.destroy
+ */
+static void rw_destroy(private_rwlock_t *this)
+{
+       this->mutex->destroy(this->mutex);
+       this->writers->destroy(this->writers);
+       this->readers->destroy(this->readers);
+       profiler_cleanup(&this->profile);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+rwlock_t *rwlock_create(rwlock_type_t type)
+{
+       switch (type)
+       {
+               case RWLOCK_TYPE_DEFAULT:
+               default:
+               {
+                       private_rwlock_t *this = malloc_thing(private_rwlock_t);
+
+                       this->public.read_lock = (void(*)(rwlock_t*))read_lock;
+                       this->public.write_lock = (void(*)(rwlock_t*))write_lock;
+                       this->public.try_write_lock = (bool(*)(rwlock_t*))try_write_lock;
+                       this->public.unlock = (void(*)(rwlock_t*))rw_unlock;
+                       this->public.destroy = (void(*)(rwlock_t*))rw_destroy;
+
+                       this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
+                       this->writers = condvar_create(CONDVAR_TYPE_DEFAULT);
+                       this->readers = condvar_create(CONDVAR_TYPE_DEFAULT);
+                       this->waiting_writers = 0;
+                       this->reader_count = 0;
+                       this->writer = 0;
+
+                       profiler_init(&this->profile);
+
+                       return &this->public;
+               }
+       }
+}
+
+#endif /* HAVE_PTHREAD_RWLOCK_INIT */
+
index 2f681b7..2f4330f 100644 (file)
@@ -31,11 +31,47 @@ struct private_rwlock_t {
         */
        rwlock_t public;
 
+#ifdef HAVE_PTHREAD_RWLOCK_INIT
+
        /**
         * wrapped pthread rwlock
         */
        pthread_rwlock_t rwlock;
 
+#else
+
+       /**
+        * mutex to emulate a native rwlock
+        */
+       mutex_t *mutex;
+
+       /**
+        * condvar to handle writers
+        */
+       condvar_t *writers;
+
+       /**
+        * condvar to handle readers
+        */
+       condvar_t *readers;
+
+       /**
+        * number of waiting writers
+        */
+       u_int waiting_writers;
+
+       /**
+        * number of readers holding the lock
+        */
+       u_int reader_count;
+
+       /**
+        * current writer thread, if any
+        */
+       pthread_t writer;
+
+#endif /* HAVE_PTHREAD_RWLOCK_INIT */
+
        /**
         * profiling info, if enabled
         */