diff options
Diffstat (limited to 'src/libstrongswan/threading')
-rw-r--r-- | src/libstrongswan/threading/condvar.h | 96 | ||||
-rw-r--r-- | src/libstrongswan/threading/lock_profiler.h | 102 | ||||
-rw-r--r-- | src/libstrongswan/threading/mutex.c | 375 | ||||
-rw-r--r-- | src/libstrongswan/threading/mutex.h | 68 | ||||
-rw-r--r-- | src/libstrongswan/threading/rwlock.c | 327 | ||||
-rw-r--r-- | src/libstrongswan/threading/rwlock.h | 80 | ||||
-rw-r--r-- | src/libstrongswan/threading/thread.c | 440 | ||||
-rw-r--r-- | src/libstrongswan/threading/thread.h | 187 | ||||
-rw-r--r-- | src/libstrongswan/threading/thread_value.c | 78 | ||||
-rw-r--r-- | src/libstrongswan/threading/thread_value.h | 68 |
10 files changed, 1821 insertions, 0 deletions
diff --git a/src/libstrongswan/threading/condvar.h b/src/libstrongswan/threading/condvar.h new file mode 100644 index 000000000..48c949c7c --- /dev/null +++ b/src/libstrongswan/threading/condvar.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup condvar condvar + * @{ @ingroup threading + */ + +#ifndef THREADING_CONDVAR_H_ +#define THREADING_CONDVAR_H_ + +typedef struct condvar_t condvar_t; +typedef enum condvar_type_t condvar_type_t; + +#include "mutex.h" + +/** + * Type of condvar. + */ +enum condvar_type_t { + /** default condvar */ + CONDVAR_TYPE_DEFAULT = 0, +}; + +/** + * Condvar wrapper to use in conjunction with mutex_t. + */ +struct condvar_t { + + /** + * Wait on a condvar until it gets signalized. + * + * @param mutex mutex to release while waiting + */ + void (*wait)(condvar_t *this, mutex_t *mutex); + + /** + * Wait on a condvar until it gets signalized, or times out. + * + * @param mutex mutex to release while waiting + * @param timeout timeout im ms + * @return TRUE if timed out, FALSE otherwise + */ + bool (*timed_wait)(condvar_t *this, mutex_t *mutex, u_int timeout); + + /** + * Wait on a condvar until it gets signalized, or times out. + * + * The passed timeval should be calculated based on the time_monotonic() + * function. + * + * @param mutex mutex to release while waiting + * @param tv absolute time until timeout + * @return TRUE if timed out, FALSE otherwise + */ + bool (*timed_wait_abs)(condvar_t *this, mutex_t *mutex, timeval_t tv); + + /** + * Wake up a single thread in a condvar. + */ + void (*signal)(condvar_t *this); + + /** + * Wake up all threads in a condvar. + */ + void (*broadcast)(condvar_t *this); + + /** + * Destroy a condvar and free its resources. + */ + void (*destroy)(condvar_t *this); +}; + +/** + * Create a condvar instance. + * + * @param type type of condvar to create + * @return condvar instance + */ +condvar_t *condvar_create(condvar_type_t type); + +#endif /** THREADING_CONDVAR_H_ @} */ + diff --git a/src/libstrongswan/threading/lock_profiler.h b/src/libstrongswan/threading/lock_profiler.h new file mode 100644 index 000000000..b64453ba1 --- /dev/null +++ b/src/libstrongswan/threading/lock_profiler.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef THREADING_LOCK_PROFILER_H_ +#define THREADING_LOCK_PROFILER_H_ + +#ifdef LOCK_PROFILER + +#include <time.h> + +/** + * Do not report mutexes with an overall waiting time smaller than this (in us) + */ +#define PROFILE_WAIT_TRESHHOLD 10000 + +/** + * Do not report mutexes with an overall lock count smaller than this + */ +#define PROFILE_LOCK_TRESHHOLD 1000 + +#include <utils/backtrace.h> + +typedef struct lock_profile_t lock_profile_t; + +struct lock_profile_t { + /** + * how long threads have waited for the lock in this mutex so far + */ + timeval_t waited; + + /** + * How many times the lock has been invoked + */ + u_int locked; + + /** + * backtrace where mutex has been created + */ + backtrace_t *backtrace; +}; + +/** + * Print and cleanup mutex profiler + */ +static inline void profiler_cleanup(lock_profile_t *profile) +{ + if (profile->waited.tv_sec > 0 || + profile->waited.tv_usec > PROFILE_WAIT_TRESHHOLD || + profile->locked > PROFILE_LOCK_TRESHHOLD) + { + fprintf(stderr, "%d.%03ds / %d times in lock created at:", + profile->waited.tv_sec, profile->waited.tv_usec, profile->locked); + profile->backtrace->log(profile->backtrace, stderr); + } + profile->backtrace->destroy(profile->backtrace); +} + +/** + * Initialize mutex profiler + */ +static inline void profiler_init(lock_profile_t *profile) +{ + profile->backtrace = backtrace_create(2); + timerclear(&profile->waited); + profile->locked = 0; +} + +#define profiler_start(profile) { \ + struct timeval _start, _end, _diff; \ + (profile)->locked++; \ + time_monotonic(&_start); + +#define profiler_end(profile) \ + time_monotonic(&_end); \ + timersub(&_end, &_start, &_diff); \ + timeradd(&(profile)->waited, &_diff, &(profile)->waited); } + +#else /* !LOCK_PROFILER */ + +#define lock_profile_t struct {} +#define profiler_cleanup(...) {} +#define profiler_init(...) {} +#define profiler_start(...) {} +#define profiler_end(...) {} + +#endif /* LOCK_PROFILER */ + +#endif /* THREADING_LOCK_PROFILER_H_ */ + diff --git a/src/libstrongswan/threading/mutex.c b/src/libstrongswan/threading/mutex.c new file mode 100644 index 000000000..a35695624 --- /dev/null +++ b/src/libstrongswan/threading/mutex.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include <pthread.h> +#include <stdint.h> +#include <time.h> +#include <errno.h> + +#include <library.h> +#include <debug.h> + +#include "condvar.h" +#include "mutex.h" +#include "lock_profiler.h" + +typedef struct private_mutex_t private_mutex_t; +typedef struct private_r_mutex_t private_r_mutex_t; +typedef struct private_condvar_t private_condvar_t; + +/** + * private data of mutex + */ +struct private_mutex_t { + + /** + * public functions + */ + mutex_t public; + + /** + * wrapped pthread mutex + */ + pthread_mutex_t mutex; + + /** + * is this a recursiv emutex, implementing private_r_mutex_t? + */ + bool recursive; + + /** + * profiling info, if enabled + */ + lock_profile_t profile; +}; + +/** + * private data of mutex, extended by recursive locking information + */ +struct private_r_mutex_t { + + /** + * Extends private_mutex_t + */ + private_mutex_t generic; + + /** + * thread which currently owns mutex + */ + pthread_t thread; + + /** + * times we have locked the lock, stored per thread + */ + pthread_key_t times; +}; + +/** + * private data of condvar + */ +struct private_condvar_t { + + /** + * public functions + */ + condvar_t public; + + /** + * wrapped pthread condvar + */ + pthread_cond_t condvar; + +}; + + + +/** + * Implementation of mutex_t.lock. + */ +static void lock(private_mutex_t *this) +{ + int err; + + profiler_start(&this->profile); + err = pthread_mutex_lock(&this->mutex); + if (err) + { + DBG1("!!! MUTEX LOCK ERROR: %s !!!", strerror(err)); + } + profiler_end(&this->profile); +} + +/** + * Implementation of mutex_t.unlock. + */ +static void unlock(private_mutex_t *this) +{ + int err; + + err = pthread_mutex_unlock(&this->mutex); + if (err) + { + DBG1("!!! MUTEX UNLOCK ERROR: %s !!!", strerror(err)); + } +} + +/** + * Implementation of mutex_t.lock. + */ +static void lock_r(private_r_mutex_t *this) +{ + pthread_t self = pthread_self(); + + if (this->thread == self) + { + uintptr_t times; + + /* times++ */ + times = (uintptr_t)pthread_getspecific(this->times); + pthread_setspecific(this->times, (void*)times + 1); + } + else + { + lock(&this->generic); + this->thread = self; + /* times = 1 */ + pthread_setspecific(this->times, (void*)1); + } +} + +/** + * Implementation of mutex_t.unlock. + */ +static void unlock_r(private_r_mutex_t *this) +{ + uintptr_t times; + + /* times-- */ + times = (uintptr_t)pthread_getspecific(this->times); + pthread_setspecific(this->times, (void*)--times); + + if (times == 0) + { + this->thread = 0; + unlock(&this->generic); + } +} + +/** + * Implementation of mutex_t.destroy + */ +static void mutex_destroy(private_mutex_t *this) +{ + profiler_cleanup(&this->profile); + pthread_mutex_destroy(&this->mutex); + free(this); +} + +/** + * Implementation of mutex_t.destroy for recursive mutex' + */ +static void mutex_destroy_r(private_r_mutex_t *this) +{ + profiler_cleanup(&this->generic.profile); + pthread_mutex_destroy(&this->generic.mutex); + pthread_key_delete(this->times); + free(this); +} + +/* + * see header file + */ +mutex_t *mutex_create(mutex_type_t type) +{ + switch (type) + { + case MUTEX_TYPE_RECURSIVE: + { + private_r_mutex_t *this = malloc_thing(private_r_mutex_t); + + this->generic.public.lock = (void(*)(mutex_t*))lock_r; + this->generic.public.unlock = (void(*)(mutex_t*))unlock_r; + this->generic.public.destroy = (void(*)(mutex_t*))mutex_destroy_r; + + pthread_mutex_init(&this->generic.mutex, NULL); + pthread_key_create(&this->times, NULL); + this->generic.recursive = TRUE; + profiler_init(&this->generic.profile); + this->thread = 0; + + return &this->generic.public; + } + case MUTEX_TYPE_DEFAULT: + default: + { + private_mutex_t *this = malloc_thing(private_mutex_t); + + this->public.lock = (void(*)(mutex_t*))lock; + this->public.unlock = (void(*)(mutex_t*))unlock; + this->public.destroy = (void(*)(mutex_t*))mutex_destroy; + + pthread_mutex_init(&this->mutex, NULL); + this->recursive = FALSE; + profiler_init(&this->profile); + + return &this->public; + } + } +} + + + +/** + * Implementation of condvar_t.wait. + */ +static void _wait(private_condvar_t *this, private_mutex_t *mutex) +{ + if (mutex->recursive) + { + private_r_mutex_t* recursive = (private_r_mutex_t*)mutex; + + /* mutex owner gets cleared during condvar wait */ + recursive->thread = 0; + pthread_cond_wait(&this->condvar, &mutex->mutex); + recursive->thread = pthread_self(); + } + else + { + pthread_cond_wait(&this->condvar, &mutex->mutex); + } +} + +/* use the monotonic clock based version of this function if available */ +#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC +#define pthread_cond_timedwait pthread_cond_timedwait_monotonic +#endif + +/** + * Implementation of condvar_t.timed_wait_abs. + */ +static bool timed_wait_abs(private_condvar_t *this, private_mutex_t *mutex, + timeval_t time) +{ + struct timespec ts; + bool timed_out; + + ts.tv_sec = time.tv_sec; + ts.tv_nsec = time.tv_usec * 1000; + + if (mutex->recursive) + { + private_r_mutex_t* recursive = (private_r_mutex_t*)mutex; + + recursive->thread = 0; + timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex, + &ts) == ETIMEDOUT; + recursive->thread = pthread_self(); + } + else + { + timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex, + &ts) == ETIMEDOUT; + } + return timed_out; +} + +/** + * Implementation of condvar_t.timed_wait. + */ +static bool timed_wait(private_condvar_t *this, private_mutex_t *mutex, + u_int timeout) +{ + timeval_t tv; + u_int s, ms; + + time_monotonic(&tv); + + s = timeout / 1000; + ms = timeout % 1000; + + tv.tv_sec += s; + tv.tv_usec += ms * 1000; + + if (tv.tv_usec > 1000000 /* 1s */) + { + tv.tv_usec -= 1000000; + tv.tv_sec++; + } + return timed_wait_abs(this, mutex, tv); +} + +/** + * Implementation of condvar_t.signal. + */ +static void _signal(private_condvar_t *this) +{ + pthread_cond_signal(&this->condvar); +} + +/** + * Implementation of condvar_t.broadcast. + */ +static void broadcast(private_condvar_t *this) +{ + pthread_cond_broadcast(&this->condvar); +} + +/** + * Implementation of condvar_t.destroy + */ +static void condvar_destroy(private_condvar_t *this) +{ + pthread_cond_destroy(&this->condvar); + free(this); +} + +/* + * see header file + */ +condvar_t *condvar_create(condvar_type_t type) +{ + switch (type) + { + case CONDVAR_TYPE_DEFAULT: + default: + { + private_condvar_t *this = malloc_thing(private_condvar_t); + + this->public.wait = (void(*)(condvar_t*, mutex_t *mutex))_wait; + this->public.timed_wait = (bool(*)(condvar_t*, mutex_t *mutex, u_int timeout))timed_wait; + this->public.timed_wait_abs = (bool(*)(condvar_t*, mutex_t *mutex, timeval_t time))timed_wait_abs; + this->public.signal = (void(*)(condvar_t*))_signal; + this->public.broadcast = (void(*)(condvar_t*))broadcast; + this->public.destroy = (void(*)(condvar_t*))condvar_destroy; + +#ifdef HAVE_PTHREAD_CONDATTR_INIT + { + pthread_condattr_t condattr; + pthread_condattr_init(&condattr); +#ifdef HAVE_CONDATTR_CLOCK_MONOTONIC + pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); +#endif + pthread_cond_init(&this->condvar, &condattr); + pthread_condattr_destroy(&condattr); + } +#endif + + return &this->public; + } + } +} + diff --git a/src/libstrongswan/threading/mutex.h b/src/libstrongswan/threading/mutex.h new file mode 100644 index 000000000..ac36b6a25 --- /dev/null +++ b/src/libstrongswan/threading/mutex.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup mutex mutex + * @{ @ingroup threading + */ + +#ifndef THREADING_MUTEX_H_ +#define THREADING_MUTEX_H_ + +typedef struct mutex_t mutex_t; +typedef enum mutex_type_t mutex_type_t; + +/** + * Type of mutex. + */ +enum mutex_type_t { + /** default mutex */ + MUTEX_TYPE_DEFAULT = 0, + /** allow recursive locking of the mutex */ + MUTEX_TYPE_RECURSIVE = 1, +}; + +/** + * Mutex wrapper implements simple, portable and advanced mutex functions. + */ +struct mutex_t { + + /** + * Acquire the lock to the mutex. + */ + void (*lock)(mutex_t *this); + + /** + * Release the lock on the mutex. + */ + void (*unlock)(mutex_t *this); + + /** + * Destroy a mutex instance. + */ + void (*destroy)(mutex_t *this); +}; + +/** + * Create a mutex instance. + * + * @param type type of mutex to create + * @return unlocked mutex instance + */ +mutex_t *mutex_create(mutex_type_t type); + +#endif /** THREADING_MUTEX_H_ @} */ + diff --git a/src/libstrongswan/threading/rwlock.c b/src/libstrongswan/threading/rwlock.c new file mode 100644 index 000000000..ee9fb10be --- /dev/null +++ b/src/libstrongswan/threading/rwlock.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include <pthread.h> + +#include <library.h> +#include <debug.h> + +#include "rwlock.h" +#include "condvar.h" +#include "mutex.h" +#include "lock_profiler.h" + +typedef struct private_rwlock_t private_rwlock_t; + +/** + * private data of rwlock + */ +struct private_rwlock_t { + + /** + * public functions + */ + 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 + */ + lock_profile_t profile; +}; + + +#ifdef HAVE_PTHREAD_RWLOCK_INIT + +/** + * Implementation of rwlock_t.read_lock + */ +static void read_lock(private_rwlock_t *this) +{ + int err; + + profiler_start(&this->profile); + err = pthread_rwlock_rdlock(&this->rwlock); + if (err != 0) + { + DBG1("!!! RWLOCK READ LOCK ERROR: %s !!!", strerror(err)); + } + profiler_end(&this->profile); +} + +/** + * Implementation of rwlock_t.write_lock + */ +static void write_lock(private_rwlock_t *this) +{ + int err; + + profiler_start(&this->profile); + err = pthread_rwlock_wrlock(&this->rwlock); + if (err != 0) + { + DBG1("!!! RWLOCK WRITE LOCK ERROR: %s !!!", strerror(err)); + } + profiler_end(&this->profile); +} + +/** + * Implementation of rwlock_t.try_write_lock + */ +static bool try_write_lock(private_rwlock_t *this) +{ + return pthread_rwlock_trywrlock(&this->rwlock) == 0; +} + +/** + * Implementation of rwlock_t.unlock + */ +static void rw_unlock(private_rwlock_t *this) +{ + int err; + + err = pthread_rwlock_unlock(&this->rwlock); + if (err != 0) + { + DBG1("!!! RWLOCK UNLOCK ERROR: %s !!!", strerror(err)); + } +} + +/** + * Implementation of rwlock_t.destroy + */ +static void rw_destroy(private_rwlock_t *this) +{ + pthread_rwlock_destroy(&this->rwlock); + 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; + + pthread_rwlock_init(&this->rwlock, NULL); + profiler_init(&this->profile); + + return &this->public; + } + } +} + +#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 new file mode 100644 index 000000000..a86a241c5 --- /dev/null +++ b/src/libstrongswan/threading/rwlock.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup rwlock rwlock + * @{ @ingroup threading + */ + +#ifndef THREADING_RWLOCK_H_ +#define THREADING_RWLOCK_H_ + +typedef struct rwlock_t rwlock_t; +typedef enum rwlock_type_t rwlock_type_t; + +/** + * Type of read-write lock. + */ +enum rwlock_type_t { + /** default condvar */ + RWLOCK_TYPE_DEFAULT = 0, +}; + +/** + * Read-Write lock wrapper. + */ +struct rwlock_t { + + /** + * Acquire the read lock. + */ + void (*read_lock)(rwlock_t *this); + + /** + * Acquire the write lock. + */ + void (*write_lock)(rwlock_t *this); + + /** + * Try to acquire the write lock. + * + * Never blocks, but returns FALSE if the lock was already occupied. + * + * @return TRUE if lock acquired + */ + bool (*try_write_lock)(rwlock_t *this); + + /** + * Release any acquired lock. + */ + void (*unlock)(rwlock_t *this); + + /** + * Destroy the read-write lock. + */ + void (*destroy)(rwlock_t *this); +}; + +/** + * Create a read-write lock instance. + * + * @param type type of rwlock to create + * @return unlocked rwlock instance + */ +rwlock_t *rwlock_create(rwlock_type_t type); + +#endif /** THREADING_RWLOCK_H_ @} */ + diff --git a/src/libstrongswan/threading/thread.c b/src/libstrongswan/threading/thread.c new file mode 100644 index 000000000..bbfb2c2c6 --- /dev/null +++ b/src/libstrongswan/threading/thread.c @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include <pthread.h> +#include <signal.h> +#include <semaphore.h> + +#include <library.h> +#include <debug.h> + +#include <threading/thread_value.h> +#include <threading/mutex.h> +#include <utils/linked_list.h> + +#include "thread.h" + +typedef struct private_thread_t private_thread_t; + +struct private_thread_t { + /** + * Public interface. + */ + thread_t public; + + /** + * Human-readable ID of this thread. + */ + u_int id; + + /** + * ID of the underlying thread. + */ + pthread_t thread_id; + + /** + * Main function of this thread (NULL for the main thread). + */ + thread_main_t main; + + /** + * Argument for the main function. + */ + void *arg; + + /** + * Stack of cleanup handlers. + */ + linked_list_t *cleanup_handlers; + + /** + * Mutex to make modifying thread properties safe. + */ + mutex_t *mutex; + + /** + * Semaphore used to sync the creation/start of the thread. + */ + sem_t created; + + /** + * TRUE if this thread has been detached or joined, i.e. can be cleaned + * up after terminating. + */ + bool detached_or_joined; + + /** + * TRUE if the threads has terminated (cancelled, via thread_exit or + * returned from the main function) + */ + bool terminated; + +}; + +typedef struct { + /** + * Cleanup callback function. + */ + thread_cleanup_t cleanup; + + /** + * Argument provided to the cleanup function. + */ + void *arg; + +} cleanup_handler_t; + + +/** + * Next thread ID. + */ +static u_int next_id = 1; + +/** + * Mutex to safely access the next thread ID. + */ +static mutex_t *id_mutex; + +/** + * Store the thread object in a thread-specific value. + */ +static thread_value_t *current_thread; + +#ifndef HAVE_PTHREAD_CANCEL +/* if pthread_cancel is not available, we emulate it using a signal */ +#define SIG_CANCEL (SIGRTMIN+7) + +/* the signal handler for SIG_CANCEL uses pthread_exit to terminate the + * "cancelled" thread */ +static void cancel_signal_handler(int sig) +{ + pthread_exit(NULL); +} +#endif + + +/** + * Destroy an internal thread object. + * + * @note The mutex of this thread object has to be locked, it gets unlocked + * automatically. + */ +static void thread_destroy(private_thread_t *this) +{ + if (!this->terminated || !this->detached_or_joined) + { + this->mutex->unlock(this->mutex); + return; + } + this->cleanup_handlers->destroy(this->cleanup_handlers); + this->mutex->unlock(this->mutex); + this->mutex->destroy(this->mutex); + sem_destroy(&this->created); + free(this); +} + +/** + * Implementation of thread_t.cancel. + */ +static void cancel(private_thread_t *this) +{ + this->mutex->lock(this->mutex); + if (pthread_equal(this->thread_id, pthread_self())) + { + this->mutex->unlock(this->mutex); + DBG1("!!! CANNOT CANCEL CURRENT THREAD !!!"); + return; + } +#ifdef HAVE_PTHREAD_CANCEL + pthread_cancel(this->thread_id); +#else + pthread_kill(this->thread_id, SIG_CANCEL); +#endif /* HAVE_PTHREAD_CANCEL */ + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of thread_t.kill. + */ +static void _kill(private_thread_t *this, int sig) +{ + this->mutex->lock(this->mutex); + if (pthread_equal(this->thread_id, pthread_self())) + { + /* it might actually be possible to send a signal to pthread_self (there + * is an example in raise(3) describing that), the problem is though, + * that the thread only returns here after the signal handler has + * returned, so depending on the signal, the lock might not get + * unlocked. */ + this->mutex->unlock(this->mutex); + DBG1("!!! CANNOT SEND SIGNAL TO CURRENT THREAD !!!"); + return; + } + pthread_kill(this->thread_id, sig); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of thread_t.detach. + */ +static void detach(private_thread_t *this) +{ + this->mutex->lock(this->mutex); + pthread_detach(this->thread_id); + this->detached_or_joined = TRUE; + thread_destroy(this); +} + +/** + * Implementation of thread_t.join. + */ +static void *join(private_thread_t *this) +{ + pthread_t thread_id; + void *val; + this->mutex->lock(this->mutex); + if (pthread_equal(this->thread_id, pthread_self())) + { + this->mutex->unlock(this->mutex); + DBG1("!!! CANNOT JOIN CURRENT THREAD !!!"); + return NULL; + } + if (this->detached_or_joined) + { + this->mutex->unlock(this->mutex); + DBG1("!!! CANNOT JOIN DETACHED THREAD !!!"); + return NULL; + } + thread_id = this->thread_id; + this->detached_or_joined = TRUE; + if (this->terminated) + { + /* thread has terminated before the call to join */ + thread_destroy(this); + } + else + { + /* thread_destroy is called when the thread terminates normally */ + this->mutex->unlock(this->mutex); + } + pthread_join(thread_id, &val); + return val; +} + +/** + * Create an internal thread object. + */ +static private_thread_t *thread_create_internal() +{ + private_thread_t *this = malloc_thing(private_thread_t); + this->public.cancel = (void(*)(thread_t*))cancel; + this->public.kill = (void(*)(thread_t*,int))_kill; + this->public.detach = (void(*)(thread_t*))detach; + this->public.join = (void*(*)(thread_t*))join; + + this->id = 0; + this->thread_id = 0; + this->main = NULL; + this->arg = NULL; + this->cleanup_handlers = linked_list_create(); + this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); + sem_init(&this->created, FALSE, 0); + this->detached_or_joined = FALSE; + this->terminated = FALSE; + + return this; +} + +/** + * Main cleanup function for threads. + */ +static void thread_cleanup(private_thread_t *this) +{ + cleanup_handler_t *handler; + this->mutex->lock(this->mutex); + while (this->cleanup_handlers->remove_last(this->cleanup_handlers, + (void**)&handler) == SUCCESS) + { + handler->cleanup(handler->arg); + free(handler); + } + this->terminated = TRUE; + thread_destroy(this); +} + +/** + * Main function wrapper for threads. + */ +static void *thread_main(private_thread_t *this) +{ + void *res; + sem_wait(&this->created); + current_thread->set(current_thread, this); + pthread_cleanup_push((thread_cleanup_t)thread_cleanup, this); + res = this->main(this->arg); + pthread_cleanup_pop(TRUE); + return res; +} + +/** + * Described in header. + */ +thread_t *thread_create(thread_main_t main, void *arg) +{ + private_thread_t *this = thread_create_internal(); + this->main = main; + this->arg = arg; + if (pthread_create(&this->thread_id, NULL, (void*)thread_main, this) != 0) + { + DBG1("failed to create thread!"); + thread_destroy(this); + return NULL; + } + id_mutex->lock(id_mutex); + this->id = next_id++; + id_mutex->unlock(id_mutex); + sem_post(&this->created); + return &this->public; +} + +/** + * Described in header. + */ +thread_t *thread_current() +{ + return current_thread->get(current_thread); +} + +/** + * Described in header. + */ +u_int thread_current_id() +{ + private_thread_t *this = (private_thread_t*)thread_current(); + return this->id; +} + +/** + * Described in header. + */ +void thread_cleanup_push(thread_cleanup_t cleanup, void *arg) +{ + private_thread_t *this = (private_thread_t*)thread_current(); + cleanup_handler_t *handler; + this->mutex->lock(this->mutex); + handler = malloc_thing(cleanup_handler_t); + handler->cleanup = cleanup; + handler->arg = arg; + this->cleanup_handlers->insert_last(this->cleanup_handlers, handler); + this->mutex->unlock(this->mutex); +} + +/** + * Described in header. + */ +void thread_cleanup_pop(bool execute) +{ + private_thread_t *this = (private_thread_t*)thread_current(); + cleanup_handler_t *handler; + this->mutex->lock(this->mutex); + if (this->cleanup_handlers->remove_last(this->cleanup_handlers, + (void**)&handler) != SUCCESS) + { + this->mutex->unlock(this->mutex); + DBG1("!!! THREAD CLEANUP ERROR !!!"); + return; + } + this->mutex->unlock(this->mutex); + + if (execute) + { + handler->cleanup(handler->arg); + } + free(handler); +} + +/** + * Described in header. + */ +bool thread_cancelability(bool enable) +{ +#ifdef HAVE_PTHREAD_CANCEL + int old; + pthread_setcancelstate(enable ? PTHREAD_CANCEL_ENABLE + : PTHREAD_CANCEL_DISABLE, &old); + return old == PTHREAD_CANCEL_ENABLE; +#else + sigset_t new, old; + sigemptyset(&new); + sigaddset(&new, SIG_CANCEL); + pthread_sigmask(enable ? SIG_UNBLOCK : SIG_BLOCK, &new, &old); + return sigismember(&old, SIG_CANCEL) == 0; +#endif /* HAVE_PTHREAD_CANCEL */ +} + +/** + * Described in header. + */ +void thread_cancellation_point() +{ + bool old = thread_cancelability(TRUE); +#ifdef HAVE_PTHREAD_CANCEL + pthread_testcancel(); +#endif /* HAVE_PTHREAD_CANCEL */ + thread_cancelability(old); +} + +/** + * Described in header. + */ +void thread_exit(void *val) +{ + pthread_exit(val); +} + +/** + * Described in header. + */ +void threads_init() +{ + private_thread_t *main_thread = thread_create_internal(); + main_thread->id = 0; + main_thread->thread_id = pthread_self(); + current_thread = thread_value_create(NULL); + current_thread->set(current_thread, (void*)main_thread); + id_mutex = mutex_create(MUTEX_TYPE_DEFAULT); + +#ifndef HAVE_PTHREAD_CANCEL + { /* install a signal handler for our custom SIG_CANCEL */ + struct sigaction action = { + .sa_handler = cancel_signal_handler + }; + sigaction(SIG_CANCEL, &action, NULL); + } +#endif /* HAVE_PTHREAD_CANCEL */ +} + +/** + * Described in header. + */ +void threads_deinit() +{ + private_thread_t *main_thread = (private_thread_t*)thread_current(); + thread_destroy(main_thread); + current_thread->destroy(current_thread); + id_mutex->destroy(id_mutex); +} + diff --git a/src/libstrongswan/threading/thread.h b/src/libstrongswan/threading/thread.h new file mode 100644 index 000000000..6bf8fac79 --- /dev/null +++ b/src/libstrongswan/threading/thread.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup thread thread + * @{ @ingroup threading + */ + +#ifndef THREADING_THREAD_H_ +#define THREADING_THREAD_H_ + +typedef struct thread_t thread_t; + +#ifdef __APPLE__ +/* on Mac OS X 10.5 several system calls we use are no cancellation points. + * fortunately, select isn't one of them, so we wrap some of the others with + * calls to select(2). + */ +#include <sys/socket.h> +#include <sys/select.h> + +#define WRAP_WITH_SELECT(func, socket, ...)\ + fd_set rfds; FD_ZERO(&rfds); FD_SET(socket, &rfds);\ + if (select(socket + 1, &rfds, NULL, NULL, NULL) <= 0) { return -1; }\ + return func(socket, __VA_ARGS__) + +static inline int cancellable_accept(int socket, struct sockaddr *address, + socklen_t *address_len) +{ + WRAP_WITH_SELECT(accept, socket, address, address_len); +} +#define accept cancellable_accept +static inline int cancellable_recvfrom(int socket, void *buffer, size_t length, + int flags, struct sockaddr *address, socklen_t *address_len) +{ + WRAP_WITH_SELECT(recvfrom, socket, buffer, length, flags, address, address_len); +} +#define recvfrom cancellable_recvfrom +#endif /* __APPLE__ */ + +/** + * Main function of a thread. + * + * @param arg argument provided to constructor + * @return value provided to threads joining the terminating thread + */ +typedef void *(*thread_main_t)(void *arg); + +/** + * Cleanup callback function for a thread. + * + * @param arg argument provided to thread_cleanup_push + */ +typedef void (*thread_cleanup_t)(void *arg); + + +/** + * Thread wrapper implements simple, portable and advanced thread functions. + * + * @note All threads other than the main thread need either to be joined or + * detached by calling the corresponding method. + */ +struct thread_t { + + /** + * Cancel this thread. + */ + void (*cancel)(thread_t *this); + + /** + * Send a signal to this thread. + * + * @param sig the signal to be sent to this thread + */ + void (*kill)(thread_t *this, int sig); + + /** + * Detach this thread, this automatically destroys the thread object after + * the thread returned from its main function. + * + * @note Calling detach is like calling destroy on other objects. + */ + void (*detach)(thread_t *this); + + /** + * Join this thread, this automatically destroys the thread object + * afterwards. + * + * @note Calling join is like calling destroy on other objects. + * + * @return the value returned from the thread's main function or + * a call to exit. + */ + void *(*join)(thread_t *this); + +}; + + +/** + * Create a new thread instance. + * + * @param main thread main function + * @param arg argument provided to the main function + * @return thread instance + */ +thread_t *thread_create(thread_main_t main, void *arg); + +/** + * Get a thread object for the current thread. + * + * @return thread instance + */ +thread_t *thread_current(); + +/** + * Get the human-readable ID of the current thread. + * + * The IDs are assigned incrementally starting from 1. + * + * @return human-readable ID + */ +u_int thread_current_id(); + +/** + * Push a function onto the current thread's cleanup handler stack. + * The callback function is called whenever the thread is cancelled, exits or + * thread_cleanup_pop is called with TRUE as execute argument. + * + * @param cleanup function called on thread exit + * @param arg argument provided to the callback + */ +void thread_cleanup_push(thread_cleanup_t cleanup, void *arg); + +/** + * Remove the top function from the current thread's cleanup handler stack + * and optionally execute it. + * + * @param execute TRUE to execute the function + */ +void thread_cleanup_pop(bool execute); + +/** + * Enable or disable the cancelability of the current thread. The current + * value is returned. + * + * @param enable TRUE to enable cancelability + * @return the current state of the cancelability + */ +bool thread_cancelability(bool enable); + +/** + * Force creation of a cancellation point in the calling thread. + */ +void thread_cancellation_point(); + +/** + * Exit the current thread. + * + * @param val value provided to threads joining the current thread + */ +void thread_exit(void *val); + +/** + * Called by the main thread to initialize the thread management. + */ +void threads_init(); + +/** + * Called by the main thread to deinitialize the thread management. + */ +void threads_deinit(); + + +#endif /** THREADING_THREAD_H_ @} */ + diff --git a/src/libstrongswan/threading/thread_value.c b/src/libstrongswan/threading/thread_value.c new file mode 100644 index 000000000..8f2a8846c --- /dev/null +++ b/src/libstrongswan/threading/thread_value.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include <pthread.h> + +#include <library.h> + +#include "thread_value.h" + +typedef struct private_thread_value_t private_thread_value_t; + +struct private_thread_value_t { + /** + * Public interface. + */ + thread_value_t public; + + /** + * Key to access thread-specific values. + */ + pthread_key_t key; + +}; + + +/** + * Implementation of thread_value_t.set. + */ +static void set(private_thread_value_t *this, void *val) +{ + pthread_setspecific(this->key, val); +} + +/** + * Implementation of thread_value_t.get. + */ +static void *get(private_thread_value_t *this) +{ + return pthread_getspecific(this->key); +} + +/** + * Implementation of thread_value_t.destroy. + */ +static void destroy(private_thread_value_t *this) +{ + pthread_key_delete(this->key); + free(this); +} + + +/** + * Described in header. + */ +thread_value_t *thread_value_create(thread_cleanup_t destructor) +{ + private_thread_value_t *this = malloc_thing(private_thread_value_t); + this->public.set = (void(*)(thread_value_t*,void*))set; + this->public.get = (void*(*)(thread_value_t*))get; + this->public.destroy = (void(*)(thread_value_t*))destroy; + + pthread_key_create(&this->key, destructor); + return &this->public; +} + diff --git a/src/libstrongswan/threading/thread_value.h b/src/libstrongswan/threading/thread_value.h new file mode 100644 index 000000000..48f5f7d6b --- /dev/null +++ b/src/libstrongswan/threading/thread_value.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup thread_value thread_value + * @{ @ingroup threading + */ + +#ifndef THREADING_THREAD_VALUE_H_ +#define THREADING_THREAD_VALUE_H_ + +#include <threading/thread.h> + +typedef struct thread_value_t thread_value_t; + +/** + * Wrapper for thread-specific values. + */ +struct thread_value_t { + + /** + * Set a thread-specific value. + * + * @param val thread specific value + */ + void (*set)(thread_value_t *this, void *val); + + /** + * Get a thread-specific value. + * + * @return the value specific to the current thread + */ + void *(*get)(thread_value_t *this); + + /** + * Destroys this thread specific value wrapper. There is no check for + * non-NULL values which are currently assigned to the calling thread, no + * destructor is called. + */ + void (*destroy)(thread_value_t *this); + +}; + +/** + * Create a new thread-specific value wrapper. + * + * The optional destructor is called whenever a thread terminates, with the + * assigned value as argument. It is not called if that value is NULL. + * + * @param destructor destructor + * @return thread-specific value wrapper + */ +thread_value_t *thread_value_create(thread_cleanup_t destructor); + +#endif /** THREADING_THREAD_VALUE_H_ @} */ + |