]> git.ipfire.org Git - people/ms/strongswan.git/blobdiff - src/libstrongswan/threading/thread.c
thread: Add a function to pop and call all registered cleanup handlers
[people/ms/strongswan.git] / src / libstrongswan / threading / thread.c
index 3751bb749b6c0e10e07d79ea7df000b67034246e..7a243e8262b69dca0ad58369310f019c682b3bc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Tobias Brunner
+ * Copyright (C) 2009-2012 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
 #define _GNU_SOURCE
 #include <pthread.h>
 #include <signal.h>
-#include <semaphore.h>
+
+#ifdef HAVE_GETTID
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_GETTID
+#include <sys/syscall.h>
+static inline pid_t gettid()
+{
+       return syscall(SYS_gettid);
+}
+#endif
 
 #include <library.h>
-#include <debug.h>
+#include <utils/debug.h>
 
 #include <threading/thread_value.h>
 #include <threading/mutex.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
 
 #include "thread.h"
 
@@ -65,11 +77,6 @@ struct private_thread_t {
         */
        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.
@@ -101,7 +108,7 @@ typedef struct {
 /**
  * Next thread ID.
  */
-static u_int next_id = 1;
+static u_int next_id;
 
 /**
  * Mutex to safely access the next thread ID.
@@ -113,9 +120,14 @@ static mutex_t *id_mutex;
  */
 static thread_value_t *current_thread;
 
+
 #ifndef HAVE_PTHREAD_CANCEL
 /* if pthread_cancel is not available, we emulate it using a signal */
+#ifdef ANDROID
+#define SIG_CANCEL SIGUSR2
+#else
 #define SIG_CANCEL (SIGRTMIN+7)
+#endif
 
 /* the signal handler for SIG_CANCEL uses pthread_exit to terminate the
  * "cancelled" thread */
@@ -142,14 +154,11 @@ static void thread_destroy(private_thread_t *this)
        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)
+METHOD(thread_t, cancel, void,
+       private_thread_t *this)
 {
        this->mutex->lock(this->mutex);
        if (pthread_equal(this->thread_id, pthread_self()))
@@ -166,10 +175,8 @@ static void cancel(private_thread_t *this)
        this->mutex->unlock(this->mutex);
 }
 
-/**
- * Implementation of thread_t.kill.
- */
-static void _kill(private_thread_t *this, int sig)
+METHOD(thread_t, kill_, void,
+       private_thread_t *this, int sig)
 {
        this->mutex->lock(this->mutex);
        if (pthread_equal(this->thread_id, pthread_self()))
@@ -187,10 +194,8 @@ static void _kill(private_thread_t *this, int sig)
        this->mutex->unlock(this->mutex);
 }
 
-/**
- * Implementation of thread_t.detach.
- */
-static void detach(private_thread_t *this)
+METHOD(thread_t, detach, void,
+       private_thread_t *this)
 {
        this->mutex->lock(this->mutex);
        pthread_detach(this->thread_id);
@@ -198,13 +203,12 @@ static void detach(private_thread_t *this)
        thread_destroy(this);
 }
 
-/**
- * Implementation of thread_t.join.
- */
-static void *join(private_thread_t *this)
+METHOD(thread_t, join, void*,
+       private_thread_t *this)
 {
        pthread_t thread_id;
        void *val;
+
        this->mutex->lock(this->mutex);
        if (pthread_equal(this->thread_id, pthread_self()))
        {
@@ -231,6 +235,7 @@ static void *join(private_thread_t *this)
                this->mutex->unlock(this->mutex);
        }
        pthread_join(thread_id, &val);
+
        return val;
 }
 
@@ -239,21 +244,18 @@ static void *join(private_thread_t *this)
  */
 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;
+       private_thread_t *this;
+
+       INIT(this,
+               .public = {
+                       .cancel = _cancel,
+                       .kill = _kill_,
+                       .detach = _detach,
+                       .join = _join,
+               },
+               .cleanup_handlers = linked_list_create(),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+       );
 
        return this;
 }
@@ -281,11 +283,26 @@ static void thread_cleanup(private_thread_t *this)
 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);
+
+       /* TODO: this is not 100% portable as pthread_t is an opaque type (i.e.
+        * could be of any size, or even a struct) */
+#ifdef HAVE_GETTID
+       DBG2(DBG_LIB, "created thread %.2d [%u]",
+                this->id, gettid());
+#elif defined(WIN32)
+       DBG2(DBG_LIB, "created thread %.2d [%p]",
+                this->id, this->thread_id.p);
+#else
+       DBG2(DBG_LIB, "created thread %.2d [%lx]",
+                this->id, (u_long)this->thread_id);
+#endif
+
        res = this->main(this->arg);
        pthread_cleanup_pop(TRUE);
+
        return res;
 }
 
@@ -295,8 +312,13 @@ static void *thread_main(private_thread_t *this)
 thread_t *thread_create(thread_main_t main, void *arg)
 {
        private_thread_t *this = thread_create_internal();
+
        this->main = main;
        this->arg = arg;
+       id_mutex->lock(id_mutex);
+       this->id = next_id++;
+       id_mutex->unlock(id_mutex);
+
        if (pthread_create(&this->thread_id, NULL, (void*)thread_main, this) != 0)
        {
                DBG1(DBG_LIB, "failed to create thread!");
@@ -304,10 +326,7 @@ thread_t *thread_create(thread_main_t main, void *arg)
                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;
 }
 
@@ -316,7 +335,20 @@ thread_t *thread_create(thread_main_t main, void *arg)
  */
 thread_t *thread_current()
 {
-       return current_thread->get(current_thread);
+       private_thread_t *this;
+
+       this = (private_thread_t*)current_thread->get(current_thread);
+       if (!this)
+       {
+               this = thread_create_internal();
+
+               id_mutex->lock(id_mutex);
+               this->id = next_id++;
+               id_mutex->unlock(id_mutex);
+
+               current_thread->set(current_thread, (void*)this);
+       }
+       return &this->public;
 }
 
 /**
@@ -325,7 +357,8 @@ thread_t *thread_current()
 u_int thread_current_id()
 {
        private_thread_t *this = (private_thread_t*)thread_current();
-       return this->id;
+
+       return this ? this->id : 0;
 }
 
 /**
@@ -335,12 +368,13 @@ 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;
+
+       INIT(handler,
+               .cleanup = cleanup,
+               .arg = arg,
+       );
+
        this->cleanup_handlers->insert_last(this->cleanup_handlers, handler);
-       this->mutex->unlock(this->mutex);
 }
 
 /**
@@ -350,15 +384,13 @@ 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(DBG_LIB, "!!! THREAD CLEANUP ERROR !!!");
                return;
        }
-       this->mutex->unlock(this->mutex);
 
        if (execute)
        {
@@ -367,6 +399,23 @@ void thread_cleanup_pop(bool execute)
        free(handler);
 }
 
+/**
+ * Described in header.
+ */
+void thread_cleanup_popall()
+{
+       private_thread_t *this = (private_thread_t*)thread_current();
+       cleanup_handler_t *handler;
+
+       while (this->cleanup_handlers->get_count(this->cleanup_handlers))
+       {
+               this->cleanup_handlers->remove_last(this->cleanup_handlers,
+                                                                                       (void**)&handler);
+               handler->cleanup(handler->arg);
+               free(handler);
+       }
+}
+
 /**
  * Described in header.
  */
@@ -374,14 +423,18 @@ 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 */
 }
@@ -392,6 +445,7 @@ bool thread_cancelability(bool enable)
 void thread_cancellation_point()
 {
        bool old = thread_cancelability(TRUE);
+
 #ifdef HAVE_PTHREAD_CANCEL
        pthread_testcancel();
 #endif /* HAVE_PTHREAD_CANCEL */
@@ -406,12 +460,22 @@ void thread_exit(void *val)
        pthread_exit(val);
 }
 
+/**
+ * A dummy thread value that reserved pthread_key_t value "0". A buggy PKCS#11
+ * library mangles this key, without owning it, so we allocate it for them.
+ */
+static thread_value_t *dummy1;
+
 /**
  * Described in header.
  */
 void threads_init()
 {
        private_thread_t *main_thread = thread_create_internal();
+
+       dummy1 = thread_value_create(NULL);
+
+       next_id = 1;
        main_thread->id = 0;
        main_thread->thread_id = pthread_self();
        current_thread = thread_value_create(NULL);
@@ -434,9 +498,13 @@ void threads_init()
 void threads_deinit()
 {
        private_thread_t *main_thread = (private_thread_t*)thread_current();
+
+       dummy1->destroy(dummy1);
+
        main_thread->mutex->lock(main_thread->mutex);
+       main_thread->terminated = TRUE;
+       main_thread->detached_or_joined = TRUE;
        thread_destroy(main_thread);
        current_thread->destroy(current_thread);
        id_mutex->destroy(id_mutex);
 }
-