aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2009-12-17 15:25:37 +0100
committerTobias Brunner <tobias@strongswan.org>2009-12-23 17:02:26 +0100
commit0d5c6a28d5c14f64e83087ee78e22cf4b844781e (patch)
treec863813c7d6ec8202677ed14226856c43bf08952 /src
parent070ac5b0b79517e5414ddc21c6f46265de2a28a4 (diff)
downloadstrongswan-0d5c6a28d5c14f64e83087ee78e22cf4b844781e.tar.bz2
strongswan-0d5c6a28d5c14f64e83087ee78e22cf4b844781e.tar.xz
Adding an object-oriented wrapper for threads.
Diffstat (limited to 'src')
-rw-r--r--src/libstrongswan/threading/thread.c405
-rw-r--r--src/libstrongswan/threading/thread.h187
2 files changed, 592 insertions, 0 deletions
diff --git a/src/libstrongswan/threading/thread.c b/src/libstrongswan/threading/thread.c
new file mode 100644
index 000000000..14f92dc8c
--- /dev/null
+++ b/src/libstrongswan/threading/thread.c
@@ -0,0 +1,405 @@
+/*
+ * 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;
+
+
+/**
+ * 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;
+ }
+ pthread_cancel(this->thread_id);
+ 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.cancel.
+ */
+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.cancel.
+ */
+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)
+{
+ int old;
+ pthread_setcancelstate(enable ? PTHREAD_CANCEL_ENABLE
+ : PTHREAD_CANCEL_DISABLE, &old);
+ return old == PTHREAD_CANCEL_ENABLE;
+}
+
+/**
+ * Described in header.
+ */
+void thread_cancellation_point()
+{
+ bool old = thread_cancelability(TRUE);
+ pthread_testcancel();
+ 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);
+}
+
+/**
+ * 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_ @} */
+