summaryrefslogtreecommitdiff
path: root/src/libstrongswan/threading/windows/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/threading/windows/thread.c')
-rw-r--r--src/libstrongswan/threading/windows/thread.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/src/libstrongswan/threading/windows/thread.c b/src/libstrongswan/threading/windows/thread.c
new file mode 100644
index 000000000..e76758f8c
--- /dev/null
+++ b/src/libstrongswan/threading/windows/thread.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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.
+ */
+
+#include "thread.h"
+
+#include <utils/debug.h>
+#include <threading/spinlock.h>
+#include <threading/thread.h>
+#include <collections/hashtable.h>
+#include <collections/array.h>
+
+
+typedef struct private_thread_t private_thread_t;
+
+struct private_thread_t {
+
+ /**
+ * Public interface.
+ */
+ thread_t public;
+
+ /**
+ * GetCurrentThreadId() of thread
+ */
+ DWORD id;
+
+ /**
+ * Printable thread id returned by thread_current_id()
+ */
+ u_int tid;
+
+ /**
+ * Windows thread handle
+ */
+ HANDLE handle;
+
+ /**
+ * Main function of this thread (NULL for the main thread).
+ */
+ thread_main_t main;
+
+ /**
+ * Argument for the main function.
+ */
+ void *arg;
+
+ /**
+ * Thread return value
+ */
+ void *ret;
+
+ /**
+ * Stack of cleanup handlers, as cleanup_t
+ */
+ array_t *cleanup;
+
+ /**
+ * Thread specific values for this thread
+ */
+ hashtable_t *tls;
+
+ /**
+ * Thread terminated?
+ */
+ bool terminated;
+
+ /**
+ * Thread detached?
+ */
+ bool detached;
+
+ /**
+ * Is thread in cancellable state
+ */
+ bool cancelability;
+
+ /**
+ * Has the thread been cancelled by thread->cancel()?
+ */
+ bool canceled;
+
+ /**
+ * Did we schedule an APC to docancel()?
+ */
+ bool cancel_pending;
+
+ /**
+ * Active condition variable thread is waiting in, if any
+ */
+ CONDITION_VARIABLE *condvar;
+};
+
+/**
+ * Global list of threads, GetCurrentThreadId() => private_thread_t
+ */
+static hashtable_t *threads;
+
+/**
+ * Lock for threads table
+ */
+static spinlock_t *threads_lock;
+
+/**
+ * Counter to assign printable thread IDs
+ */
+static u_int threads_ids = 0;
+
+/**
+ * Forward declaration
+ */
+static private_thread_t *create_internal(DWORD id);
+
+/**
+ * Set leak detective state
+ */
+static inline bool set_leak_detective(bool state)
+{
+#ifdef LEAK_DETECTIVE
+ if (lib && lib->leak_detective)
+ {
+ return lib->leak_detective->set_state(lib->leak_detective, state);
+ }
+#endif
+ return FALSE;
+}
+
+/**
+ * Store thread in index
+ */
+static void put_thread(private_thread_t *this)
+{
+ bool old;
+
+ old = set_leak_detective(FALSE);
+ threads_lock->lock(threads_lock);
+
+ threads->put(threads, (void*)(uintptr_t)this->id, this);
+
+ threads_lock->unlock(threads_lock);
+ set_leak_detective(old);
+}
+
+/**
+ * Remove thread from index
+ */
+static void remove_thread(private_thread_t *this)
+{
+ bool old;
+
+ old = set_leak_detective(FALSE);
+ threads_lock->lock(threads_lock);
+
+ threads->remove(threads, (void*)(uintptr_t)this->id);
+
+ threads_lock->unlock(threads_lock);
+ set_leak_detective(old);
+}
+
+/**
+ * Get thread data for calling thread
+ */
+static private_thread_t *get_current_thread()
+{
+ private_thread_t *this;
+
+ threads_lock->lock(threads_lock);
+
+ this = threads->get(threads, (void*)(uintptr_t)GetCurrentThreadId());
+
+ threads_lock->unlock(threads_lock);
+
+ if (!this)
+ {
+ this = create_internal(GetCurrentThreadId());
+ put_thread(this);
+ }
+
+ return this;
+}
+
+/**
+ * See header.
+ */
+void* thread_tls_put(void *key, void *value)
+{
+ private_thread_t *thread;
+ bool old;
+
+ thread = get_current_thread();
+
+ old = set_leak_detective(FALSE);
+ value = thread->tls->put(thread->tls, key, value);
+ set_leak_detective(old);
+
+ return value;
+}
+
+/**
+ * See header.
+ */
+void* thread_tls_get(void *key)
+{
+ private_thread_t *thread;
+ void *value;
+ bool old;
+
+ thread = get_current_thread();
+
+ old = set_leak_detective(FALSE);
+ value = thread->tls->get(thread->tls, key);
+ set_leak_detective(old);
+
+ return value;
+}
+
+/**
+ * See header.
+ */
+void* thread_tls_remove(void *key)
+{
+ private_thread_t *thread;
+ void *value;
+ bool old;
+
+ thread = get_current_thread();
+
+ old = set_leak_detective(FALSE);
+ threads_lock->lock(threads_lock);
+ value = thread->tls->remove(thread->tls, key);
+ threads_lock->unlock(threads_lock);
+ set_leak_detective(old);
+
+ return value;
+}
+
+/**
+ * Thread cleanup data
+ */
+typedef struct {
+ /** Cleanup callback function */
+ thread_cleanup_t cb;
+ /** Argument provided to the cleanup function */
+ void *arg;
+} cleanup_t;
+
+/**
+ * Invoke pushed/tls cleanup handlers
+ */
+static void docleanup(private_thread_t *this)
+{
+ enumerator_t *enumerator;
+ cleanup_t cleanup, *tls;
+ bool old;
+
+ old = set_leak_detective(FALSE);
+
+ while (array_remove(this->cleanup, -1, &cleanup))
+ {
+ set_leak_detective(old);
+ cleanup.cb(cleanup.arg);
+ set_leak_detective(FALSE);
+ }
+
+ threads_lock->lock(threads_lock);
+ enumerator = this->tls->create_enumerator(this->tls);
+ while (enumerator->enumerate(enumerator, NULL, &tls))
+ {
+ this->tls->remove_at(this->tls, enumerator);
+
+ set_leak_detective(old);
+ thread_tls_cleanup(tls);
+ set_leak_detective(FALSE);
+ }
+ enumerator->destroy(enumerator);
+ threads_lock->unlock(threads_lock);
+
+ set_leak_detective(old);
+}
+
+/**
+ * Clean up and destroy a thread
+ */
+static void destroy(private_thread_t *this)
+{
+ bool old;
+
+ docleanup(this);
+
+ old = set_leak_detective(FALSE);
+
+ array_destroy(this->cleanup);
+ this->tls->destroy(this->tls);
+ if (this->handle)
+ {
+ CloseHandle(this->handle);
+ }
+ free(this);
+
+ set_leak_detective(old);
+}
+
+/**
+ * End a thread, destroy when detached
+ */
+static void end_thread(private_thread_t *this)
+{
+ if (this->detached)
+ {
+ remove_thread(this);
+ destroy(this);
+ }
+ else
+ {
+ this->terminated = TRUE;
+ docleanup(this);
+ }
+}
+
+/**
+ * See header.
+ */
+void thread_set_active_condvar(CONDITION_VARIABLE *condvar)
+{
+ private_thread_t *thread;
+
+ thread = get_current_thread();
+
+ threads_lock->lock(threads_lock);
+ thread->condvar = condvar;
+ threads_lock->unlock(threads_lock);
+
+ /* this is a cancellation point, as condvar wait is one */
+ SleepEx(0, TRUE);
+}
+
+/**
+ * APC to cancel a thread
+ */
+static void WINAPI docancel(ULONG_PTR dwParam)
+{
+ private_thread_t *this = (private_thread_t*)dwParam;
+
+ /* make sure cancel() does not access this anymore */
+ threads_lock->lock(threads_lock);
+ threads_lock->unlock(threads_lock);
+
+ end_thread(this);
+ ExitThread(0);
+}
+
+METHOD(thread_t, cancel, void,
+ private_thread_t *this)
+{
+ this->canceled = TRUE;
+ if (this->cancelability)
+ {
+ threads_lock->lock(threads_lock);
+ if (!this->cancel_pending)
+ {
+ this->cancel_pending = TRUE;
+ QueueUserAPC(docancel, this->handle, (uintptr_t)this);
+ if (this->condvar)
+ {
+ WakeAllConditionVariable(this->condvar);
+ }
+ }
+ threads_lock->unlock(threads_lock);
+ }
+}
+
+METHOD(thread_t, kill_, void,
+ private_thread_t *this, int sig)
+{
+}
+
+METHOD(thread_t, detach, void,
+ private_thread_t *this)
+{
+ this->detached = TRUE;
+}
+
+METHOD(thread_t, join, void*,
+ private_thread_t *this)
+{
+ void *ret;
+
+ if (this->detached)
+ {
+ return NULL;
+ }
+
+ while (!this->terminated)
+ {
+ /* join is a cancellation point, use alertable wait */
+ WaitForSingleObjectEx(this->handle, INFINITE, TRUE);
+ }
+
+ ret = this->ret;
+
+ remove_thread(this);
+ destroy(this);
+
+ return ret;
+}
+
+/**
+ * Main function wrapper for threads
+ */
+static DWORD thread_cb(private_thread_t *this)
+{
+ /* Enable cancelability once the thread starts. We must check for any
+ * pending cancellation request an queue the APC that gets executed
+ * at the first cancellation point. */
+ this->cancelability = TRUE;
+ if (this->canceled)
+ {
+ cancel(this);
+ }
+
+ this->ret = this->main(this->arg);
+
+ end_thread(this);
+
+ return 0;
+}
+
+/**
+ * Create an internal thread object.
+ */
+static private_thread_t *create_internal(DWORD id)
+{
+ private_thread_t *this;
+ bool old;
+
+ old = set_leak_detective(FALSE);
+
+ INIT(this,
+ .public = {
+ .cancel = _cancel,
+ .kill = _kill_,
+ .detach = _detach,
+ .join = _join,
+ },
+ .cleanup = array_create(sizeof(cleanup_t), 0),
+ .tls = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
+ .id = id,
+ .cancelability = TRUE,
+ );
+
+ set_leak_detective(old);
+
+ threads_lock->lock(threads_lock);
+ this->tid = threads_ids++;
+ threads_lock->unlock(threads_lock);
+
+ if (id)
+ {
+ this->handle = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
+ }
+ return this;
+}
+
+/**
+ * Described in header.
+ */
+thread_t *thread_create(thread_main_t main, void *arg)
+{
+ private_thread_t *this;
+
+ this = create_internal(0);
+
+ this->main = main;
+ this->arg = arg;
+ /* not cancellable until started */
+ this->cancelability = FALSE;
+
+ this->handle = CreateThread(NULL, 0, (void*)thread_cb, this,
+ CREATE_SUSPENDED, &this->id);
+ if (!this->handle)
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ put_thread(this);
+
+ DBG2(DBG_LIB, "created thread %u", this->id);
+
+ ResumeThread(this->handle);
+
+ return &this->public;
+}
+
+/**
+ * Described in header.
+ */
+thread_t *thread_current()
+{
+ return &get_current_thread()->public;
+}
+
+/**
+ * Described in header.
+ */
+u_int thread_current_id()
+{
+ return get_current_thread()->tid;
+}
+
+/**
+ * Described in header.
+ */
+void thread_cleanup_push(thread_cleanup_t cb, void *arg)
+{
+ private_thread_t *this;
+ cleanup_t cleanup = {
+ .cb = cb,
+ .arg = arg,
+ };
+ bool old;
+
+ this = get_current_thread();
+
+ old = set_leak_detective(FALSE);
+ array_insert(this->cleanup, -1, &cleanup);
+ set_leak_detective(old);
+}
+
+/**
+ * Described in header
+ */
+void thread_cleanup_pop(bool execute)
+{
+ private_thread_t *this;
+ cleanup_t cleanup = {};
+ bool old;
+
+ this = get_current_thread();
+
+ old = set_leak_detective(FALSE);
+ array_remove(this->cleanup, -1, &cleanup);
+ set_leak_detective(old);
+
+ if (execute)
+ {
+ cleanup.cb(cleanup.arg);
+ }
+}
+
+/**
+ * Described in header.
+ */
+bool thread_cancelability(bool enable)
+{
+ private_thread_t *this;
+ bool old;
+
+ this = get_current_thread();
+ old = this->cancelability;
+ this->cancelability = enable;
+
+ if (enable && !old && this->canceled)
+ {
+ cancel(this);
+ }
+ return old;
+}
+
+/**
+ * Described in header.
+ */
+void thread_cancellation_point()
+{
+ bool old;
+
+ old = thread_cancelability(TRUE);
+ SleepEx(0, TRUE);
+ thread_cancelability(old);
+}
+
+/**
+ * Described in header.
+ */
+void thread_exit(void *val)
+{
+ private_thread_t *this;
+
+ this = get_current_thread();
+ this->ret = val;
+
+ end_thread(this);
+ ExitThread(0);
+}
+
+/**
+ * Clean up thread data while it detaches
+ */
+static void cleanup_tls()
+{
+ private_thread_t *this;
+ bool old;
+
+ old = set_leak_detective(FALSE);
+ threads_lock->lock(threads_lock);
+
+ this = threads->remove(threads, (void*)(uintptr_t)GetCurrentThreadId());
+
+ threads_lock->unlock(threads_lock);
+ set_leak_detective(old);
+
+ if (this)
+ {
+ /* If the thread exited, but has not been joined, it is in terminated
+ * state. We must not mangle it, as we target externally spawned
+ * threads only. */
+ if (!this->terminated && !this->detached)
+ {
+ destroy(this);
+ }
+ }
+}
+
+/**
+ * DllMain called for dll events
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_THREAD_DETACH:
+ cleanup_tls();
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+/*
+ * Described in header.
+ */
+void threads_init()
+{
+ threads_lock = spinlock_create();
+ threads = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4);
+
+ /* reset counter should we initialize more than once */
+ threads_ids = 0;
+
+ put_thread(create_internal(GetCurrentThreadId()));
+}
+
+/**
+ * Described in header.
+ */
+void threads_deinit()
+{
+ private_thread_t *this;
+
+ this = threads->remove(threads, (void*)(uintptr_t)GetCurrentThreadId());
+ destroy(this);
+
+ threads_lock->destroy(threads_lock);
+ threads->destroy(threads);
+}