diff --git a/configure.in b/configure.in index 2c2ffaf02..cb3910a64 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/src/libstrongswan/threading/rwlock.c b/src/libstrongswan/threading/rwlock.c index a11369d85..bf59f50f1 100644 --- a/src/libstrongswan/threading/rwlock.c +++ b/src/libstrongswan/threading/rwlock.c @@ -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 */ + diff --git a/src/libstrongswan/threading/rwlock.h b/src/libstrongswan/threading/rwlock.h index 2f681b781..2f4330ffb 100644 --- a/src/libstrongswan/threading/rwlock.h +++ b/src/libstrongswan/threading/rwlock.h @@ -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 */