]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Adding an object-oriented wrapper for threads.
authorTobias Brunner <tobias@strongswan.org>
Thu, 17 Dec 2009 14:25:37 +0000 (15:25 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 23 Dec 2009 16:02:26 +0000 (17:02 +0100)
src/libstrongswan/threading/thread.c [new file with mode: 0644]
src/libstrongswan/threading/thread.h [new file with mode: 0644]

diff --git a/src/libstrongswan/threading/thread.c b/src/libstrongswan/threading/thread.c
new file mode 100644 (file)
index 0000000..14f92dc
--- /dev/null
@@ -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 (file)
index 0000000..6bf8fac
--- /dev/null
@@ -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_ @} */
+