windows: Provide a complete native Windows threading backend
authorMartin Willi <martin@revosec.ch>
Mon, 21 Oct 2013 16:07:51 +0000 (18:07 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Jun 2014 13:53:01 +0000 (15:53 +0200)
src/libstrongswan/Makefile.am
src/libstrongswan/threading/windows/mutex.c [new file with mode: 0644]
src/libstrongswan/threading/windows/rwlock.c [new file with mode: 0644]
src/libstrongswan/threading/windows/semaphore.c [new file with mode: 0644]
src/libstrongswan/threading/windows/spinlock.c [new file with mode: 0644]
src/libstrongswan/threading/windows/thread.c [new file with mode: 0644]
src/libstrongswan/threading/windows/thread.h [new file with mode: 0644]
src/libstrongswan/threading/windows/thread_value.c [new file with mode: 0644]

index df1f026..2602a9e 100644 (file)
@@ -33,14 +33,18 @@ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
 processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
 selectors/traffic_selector.c settings/settings.c settings/settings_types.c \
 settings/settings_parser.y settings/settings_lexer.l \
-threading/thread.c threading/thread_value.c threading/mutex.c \
-threading/semaphore.c threading/rwlock.c threading/spinlock.c \
 utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
 utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
 utils/parser_helper.c utils/test.c utils/utils/strerror.c
 
 if !USE_WINDOWS
   libstrongswan_la_SOURCES += \
+    threading/thread.c \
+    threading/thread_value.c \
+    threading/mutex.c \
+    threading/rwlock.c \
+    threading/spinlock.c \
+    threading/semaphore.c \
     networking/streams/stream_unix.c \
     networking/streams/stream_service_unix.c
 endif
@@ -89,7 +93,8 @@ resolver/rr.h resolver/resolver_manager.h \
 plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \
 processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
 processing/scheduler.h processing/watcher.h selectors/traffic_selector.h \
-settings/settings.h threading/thread.h threading/thread_value.h \
+settings/settings.h threading/thread_value.h \
+threading/thread.h threading/windows/thread.h \
 threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
 threading/rwlock.h threading/rwlock_condvar.h threading/lock_profiler.h \
 utils/utils.h utils/chunk.h utils/debug.h utils/enum.h utils/identification.h \
@@ -102,7 +107,7 @@ endif
 
 library.lo :   $(top_builddir)/config.status
 
-libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB) $(UNWINDLIB)
+libstrongswan_la_LIBADD = $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB) $(UNWINDLIB)
 
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/libstrongswan \
@@ -122,7 +127,15 @@ AM_YFLAGS = -v -d
 if USE_WINDOWS
   libstrongswan_la_LIBADD += -lws2_32
   libstrongswan_la_SOURCES += \
+    threading/windows/thread.c \
+    threading/windows/thread_value.c \
+    threading/windows/mutex.c \
+    threading/windows/rwlock.c \
+    threading/windows/spinlock.c \
+    threading/windows/semaphore.c \
     utils/windows.c
+else
+  libstrongswan_la_LIBADD += $(PTHREADLIB)
 endif
 
 if USE_DBGHELP
diff --git a/src/libstrongswan/threading/windows/mutex.c b/src/libstrongswan/threading/windows/mutex.c
new file mode 100644 (file)
index 0000000..873cb8f
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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 <library.h>
+#include <utils/debug.h>
+#include <threading/mutex.h>
+#include <threading/condvar.h>
+
+#include "thread.h"
+
+typedef struct private_mutex_t private_mutex_t;
+typedef struct private_condvar_t private_condvar_t;
+
+/**
+ * private data of mutex
+ */
+struct private_mutex_t {
+
+       /**
+        * public functions
+        */
+       mutex_t public;
+
+       /**
+        * wrapped critical section
+        */
+       CRITICAL_SECTION cs;
+
+       /**
+        * Recursive lock count
+        */
+       u_int times;
+};
+
+/**
+ * private data of condvar
+ */
+struct private_condvar_t {
+
+       /**
+        * public functions
+        */
+       condvar_t public;
+
+       /**
+        * wrapped condition variable
+        */
+       CONDITION_VARIABLE cv;
+};
+
+
+METHOD(mutex_t, lock, void,
+       private_mutex_t *this)
+{
+       EnterCriticalSection(&this->cs);
+       this->times++;
+}
+
+METHOD(mutex_t, unlock, void,
+       private_mutex_t *this)
+{
+       this->times--;
+       LeaveCriticalSection(&this->cs);
+}
+
+METHOD(mutex_t, mutex_destroy, void,
+       private_mutex_t *this)
+{
+       DeleteCriticalSection(&this->cs);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+mutex_t *mutex_create(mutex_type_t type)
+{
+       private_mutex_t *this;
+
+       INIT(this,
+               .public = {
+                       .lock = _lock,
+                       .unlock = _unlock,
+                       .destroy = _mutex_destroy,
+               },
+       );
+
+       /* CriticalSections are recursive, we use it for all mutex types. */
+       InitializeCriticalSection(&this->cs);
+
+       return &this->public;
+}
+
+METHOD(condvar_t, timed_wait, bool,
+       private_condvar_t *this, mutex_t *pubmutex, u_int timeout)
+{
+       private_mutex_t *mutex = (private_mutex_t*)pubmutex;
+       u_int times;
+       bool ret;
+
+       thread_set_active_condvar(&this->cv);
+
+       /* while a CriticalSection is recursive, waiting in a condvar releases
+        * only one mutex. So release (and reaquire) all locks except the last. */
+       times = mutex->times;
+       while (mutex->times-- > 1)
+       {
+               LeaveCriticalSection(&mutex->cs);
+       }
+
+       ret = SleepConditionVariableCS(&this->cv, &mutex->cs, timeout);
+
+       while (++mutex->times < times)
+       {
+               EnterCriticalSection(&mutex->cs);
+       }
+
+       thread_set_active_condvar(NULL);
+
+       return ret == 0;
+}
+
+METHOD(condvar_t, wait_, void,
+       private_condvar_t *this, mutex_t *mutex)
+{
+       timed_wait(this, mutex, INFINITE);
+}
+
+METHOD(condvar_t, timed_wait_abs, bool,
+       private_condvar_t *this, mutex_t *mutex, timeval_t tv)
+{
+       DWORD timeout;
+       timeval_t now, diff;
+
+       time_monotonic(&now);
+       if (timercmp(&now, &tv, >))
+       {
+               return TRUE;
+       }
+       timersub(&tv, &now, &diff);
+       timeout = diff.tv_sec * 1000 + diff.tv_usec / 1000;
+
+       return timed_wait(this, mutex, timeout);
+}
+
+METHOD(condvar_t, signal_, void,
+       private_condvar_t *this)
+{
+       WakeConditionVariable(&this->cv);
+}
+
+METHOD(condvar_t, broadcast, void,
+       private_condvar_t *this)
+{
+       WakeAllConditionVariable(&this->cv);
+}
+
+METHOD(condvar_t, condvar_destroy, void,
+       private_condvar_t *this)
+{
+       free(this);
+}
+
+/*
+ * see header file
+ */
+condvar_t *condvar_create(condvar_type_t type)
+{
+       private_condvar_t *this;
+
+       INIT(this,
+               .public = {
+                       .wait = _wait_,
+                       .timed_wait = _timed_wait,
+                       .timed_wait_abs = _timed_wait_abs,
+                       .signal = _signal_,
+                       .broadcast = _broadcast,
+                       .destroy = _condvar_destroy,
+               }
+       );
+
+       InitializeConditionVariable(&this->cv);
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/threading/windows/rwlock.c b/src/libstrongswan/threading/windows/rwlock.c
new file mode 100644 (file)
index 0000000..b27ef00
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * 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 <library.h>
+#include <utils/debug.h>
+#include <threading/rwlock.h>
+#include <threading/rwlock_condvar.h>
+#include <threading/thread_value.h>
+
+#include "thread.h"
+
+typedef struct private_rwlock_t private_rwlock_t;
+typedef struct private_rwlock_condvar_t private_rwlock_condvar_t;
+
+/**
+ * private data of rwlock
+ */
+struct private_rwlock_t {
+
+       /**
+        * public functions
+        */
+       rwlock_t public;
+
+       /**
+        * wrapped rwlock
+        */
+       SRWLOCK srw;
+
+       /**
+        * Thread specific shared lock count
+        */
+       thread_value_t *shared;
+};
+
+/**
+ * private data of condvar
+ */
+struct private_rwlock_condvar_t {
+
+       /**
+        * public interface
+        */
+       rwlock_condvar_t public;
+
+       /**
+        * condition variable
+        */
+       CONDITION_VARIABLE cv;
+};
+
+METHOD(rwlock_t, read_lock, void,
+       private_rwlock_t *this)
+{
+       uintptr_t count;
+
+       /* Recursive read locks are not supported. Use a thread specific
+        * recursiveness counter. */
+
+       count = (uintptr_t)this->shared->get(this->shared);
+       if (count == 0)
+       {
+               AcquireSRWLockShared(&this->srw);
+       }
+       this->shared->set(this->shared, (void*)(count + 1));
+}
+
+METHOD(rwlock_t, write_lock, void,
+       private_rwlock_t *this)
+{
+       AcquireSRWLockExclusive(&this->srw);
+}
+
+METHOD(rwlock_t, try_write_lock, bool,
+       private_rwlock_t *this)
+{
+       /* TODO: causes random failures and segfaults. Bug? */
+       return FALSE;
+       return TryAcquireSRWLockExclusive(&this->srw);
+}
+
+METHOD(rwlock_t, unlock, void,
+       private_rwlock_t *this)
+{
+       uintptr_t count;
+
+       count = (uintptr_t)this->shared->get(this->shared);
+       switch (count)
+       {
+               case 0:
+                       ReleaseSRWLockExclusive(&this->srw);
+                       break;
+               case 1:
+                       ReleaseSRWLockShared(&this->srw);
+                       /* fall */
+               default:
+                       this->shared->set(this->shared, (void*)(count - 1));
+                       break;
+       }
+}
+
+METHOD(rwlock_t, destroy, void,
+       private_rwlock_t *this)
+{
+       this->shared->destroy(this->shared);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+rwlock_t *rwlock_create(rwlock_type_t type)
+{
+       private_rwlock_t *this;
+
+       INIT(this,
+               .public = {
+                       .read_lock = _read_lock,
+                       .write_lock = _write_lock,
+                       .try_write_lock = _try_write_lock,
+                       .unlock = _unlock,
+                       .destroy = _destroy,
+               },
+               .shared = thread_value_create(NULL),
+       );
+
+       InitializeSRWLock(&this->srw);
+
+       return &this->public;
+}
+
+METHOD(rwlock_condvar_t, timed_wait, bool,
+       private_rwlock_condvar_t *this, rwlock_t *pubrwlock, u_int timeout)
+{
+       private_rwlock_t *rwlock = (private_rwlock_t*)pubrwlock;
+       bool ret;
+
+       thread_set_active_condvar(&this->cv);
+
+       ret = SleepConditionVariableSRW(&this->cv, &rwlock->srw, timeout, 0);
+
+       thread_set_active_condvar(NULL);
+
+       return ret == 0;
+}
+
+METHOD(rwlock_condvar_t, wait_, void,
+       private_rwlock_condvar_t *this, rwlock_t *lock)
+{
+       timed_wait(this, lock, INFINITE);
+}
+
+METHOD(rwlock_condvar_t, timed_wait_abs, bool,
+       private_rwlock_condvar_t *this, rwlock_t *lock, timeval_t tv)
+{
+       DWORD timeout;
+       timeval_t now, diff;
+
+       time_monotonic(&now);
+       if (timercmp(&now, &tv, >))
+       {
+               return TRUE;
+       }
+       timersub(&tv, &now, &diff);
+       timeout = diff.tv_sec * 1000 + diff.tv_usec / 1000;
+
+       return timed_wait(this, lock, timeout);
+}
+
+METHOD(rwlock_condvar_t, signal_, void,
+       private_rwlock_condvar_t *this)
+{
+       WakeConditionVariable(&this->cv);
+}
+
+METHOD(rwlock_condvar_t, broadcast, void,
+       private_rwlock_condvar_t *this)
+{
+       WakeAllConditionVariable(&this->cv);
+}
+
+METHOD(rwlock_condvar_t, condvar_destroy, void,
+       private_rwlock_condvar_t *this)
+{
+       free(this);
+}
+
+/*
+ * see header file
+ */
+rwlock_condvar_t *rwlock_condvar_create()
+{
+       private_rwlock_condvar_t *this;
+
+       INIT(this,
+               .public = {
+                       .wait = _wait_,
+                       .timed_wait = _timed_wait,
+                       .timed_wait_abs = _timed_wait_abs,
+                       .signal = _signal_,
+                       .broadcast = _broadcast,
+                       .destroy = _condvar_destroy,
+               },
+       );
+
+       InitializeConditionVariable(&this->cv);
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/threading/windows/semaphore.c b/src/libstrongswan/threading/windows/semaphore.c
new file mode 100644 (file)
index 0000000..29f523d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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 <library.h>
+#include <threading/semaphore.h>
+
+typedef struct private_semaphore_t private_semaphore_t;
+
+/**
+ * private data of a semaphore
+ */
+struct private_semaphore_t {
+       /**
+        * public interface
+        */
+       semaphore_t public;
+
+       /**
+        * Handle to semaphore
+        */
+       HANDLE handle;
+};
+
+METHOD(semaphore_t, timed_wait, bool,
+       private_semaphore_t *this, u_int timeout)
+{
+       /* use alertable wait to allow cancellation */
+       return WaitForSingleObjectEx(this->handle, timeout, TRUE) == WAIT_TIMEOUT;
+}
+
+METHOD(semaphore_t, timed_wait_abs, bool,
+       private_semaphore_t *this, timeval_t tv)
+{
+       DWORD timeout;
+       timeval_t now, diff;
+
+       time_monotonic(&now);
+       if (timercmp(&now, &tv, >))
+       {
+               return TRUE;
+       }
+       timersub(&tv, &now, &diff);
+       timeout = diff.tv_sec * 1000 + diff.tv_usec / 1000;
+
+       return timed_wait(this, timeout);
+}
+
+METHOD(semaphore_t, wait_, void,
+       private_semaphore_t *this)
+{
+       timed_wait(this, INFINITE);
+}
+
+METHOD(semaphore_t, post, void,
+       private_semaphore_t *this)
+{
+       ReleaseSemaphore(this->handle, 1, NULL);
+}
+
+METHOD(semaphore_t, destroy, void,
+       private_semaphore_t *this)
+{
+       CloseHandle(this->handle);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+semaphore_t *semaphore_create(u_int value)
+{
+       private_semaphore_t *this;
+
+       INIT(this,
+               .public = {
+                       .wait = _wait_,
+                       .timed_wait = _timed_wait,
+                       .timed_wait_abs = _timed_wait_abs,
+                       .post = _post,
+                       .destroy = _destroy,
+               },
+               /* our API does not have an upper limit, but Windows requires one.
+                * 0xFFFFFFF (268435455) is the highest value for which Windows does
+                * not return ERROR_INVALID_PARAMETER, and should be sufficient. */
+               .handle = CreateSemaphore(NULL, value, 0xFFFFFFF, NULL),
+       );
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/threading/windows/spinlock.c b/src/libstrongswan/threading/windows/spinlock.c
new file mode 100644 (file)
index 0000000..155dd56
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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 <library.h>
+#include <threading/spinlock.h>
+
+typedef struct private_spinlock_t private_spinlock_t;
+
+/**
+ * private data of spinlock
+ */
+struct private_spinlock_t {
+
+       /**
+        * public functions
+        */
+       spinlock_t public;
+
+       /**
+        * wrapped critical section
+        */
+       CRITICAL_SECTION cs;
+};
+
+METHOD(spinlock_t, lock, void,
+       private_spinlock_t *this)
+{
+       EnterCriticalSection(&this->cs);
+}
+
+METHOD(spinlock_t, unlock, void,
+       private_spinlock_t *this)
+{
+       LeaveCriticalSection(&this->cs);
+}
+
+METHOD(spinlock_t, destroy, void,
+       private_spinlock_t *this)
+{
+       DeleteCriticalSection(&this->cs);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+spinlock_t *spinlock_create()
+{
+       private_spinlock_t *this;
+
+       INIT(this,
+               .public = {
+                       .lock = _lock,
+                       .unlock = _unlock,
+                       .destroy = _destroy,
+               },
+       );
+
+       /* Usually the wait time in a spinlock should be short, so we could have
+        * a high spincount. But having a large/INFINITE spincount does not scale
+        * that well where a spinlock is not the perfect choice for a lock. We
+        * choose the spincount quite arbitrary, so we go to wait if it is not
+        * much more expensive than spinning. */
+       InitializeCriticalSectionAndSpinCount(&this->cs, 256);
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/threading/windows/thread.c b/src/libstrongswan/threading/windows/thread.c
new file mode 100644 (file)
index 0000000..71e5652
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * 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 <library.h>
+#include <utils/debug.h>
+#include <threading/spinlock.h>
+#include <threading/thread.h>
+#include <collections/hashtable.h>
+#include <collections/array.h>
+
+#include "thread.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, as cleanup_t
+        */
+       hashtable_t *tls;
+
+       /**
+        * Thread terminated?
+        */
+       bool terminated;
+
+       /**
+        * Thread detached?
+        */
+       bool detached;
+
+       /**
+        * Is thread in cancellable state
+        */
+       bool cancelability;
+
+       /**
+        * Has the thread been cancelled
+        */
+       bool canceled;
+
+       /**
+        * 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);
+
+       this = 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);
+       value = thread->tls->remove(thread->tls, key);
+       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);
+       }
+
+       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);
+
+       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 docancel(private_thread_t *this)
+{
+       /* 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);
+               QueueUserAPC((void*)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);
+}
+
+/*
+ * 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);
+}
diff --git a/src/libstrongswan/threading/windows/thread.h b/src/libstrongswan/threading/windows/thread.h
new file mode 100644 (file)
index 0000000..e393d18
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef WINDOWS_THREAD_H_
+#define WINDOWS_THREAD_H_
+
+/**
+ * @defgroup windowsthread windows
+ * @ingroup threading
+ *
+ * @defgroup threadwindows thread
+ * @{ @ingroup windowsthread
+ */
+
+/**
+ * MinGW seems to miss the actual struct definition
+ */
+typedef struct {
+       PVOID Ptr;
+} SRWLOCK, *PSRWLOCK, CONDITION_VARIABLE, *PCONDITION_VARIABLE;
+
+VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
+BOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable,
+                                       PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds);
+BOOL WINAPI SleepConditionVariableSRW(PCONDITION_VARIABLE ConditionVariable,
+                                               PSRWLOCK SRWLock, DWORD dwMilliseconds, ULONG Flags);
+VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable);
+VOID WINAPI WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
+VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock);
+VOID WINAPI AcquireSRWLockShared(PSRWLOCK SRWLock);
+BOOL TryAcquireSRWLockExclusive(PSRWLOCK SRWLock);
+VOID WINAPI InitializeSRWLock(PSRWLOCK SRWLock);
+VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
+VOID WINAPI ReleaseSRWLockShared(PSRWLOCK SRWLock);
+
+/**
+ * Set active condvar of a thread before waiting in it.
+ *
+ * @param cv           active condition variable, NULL to unset
+ */
+void thread_set_active_condvar(CONDITION_VARIABLE *condvar);
+
+/**
+ * Set a thread specific value on the current thread.
+ *
+ * @param key          unique key specifying the TLS variable
+ * @param value                value to set
+ * @return                     old value for key, if any
+ */
+void* thread_tls_put(void *key, void *value);
+
+/**
+ * Get a thread specific value from the current thread.
+ *
+ * @param key          unique key specifying the TLS variable
+ * @return                     value for key, if any
+ */
+void* thread_tls_get(void *key);
+
+/**
+ * Remove a thread specific value from the current thread.
+ *
+ * @param key          unique key specifying the TLS variable
+ * @param value                value to set
+ * @return                     old value for key, if any
+ */
+void* thread_tls_remove(void *key);
+
+/**
+ * Cleanup function for thread specific value.
+ *
+ * This is called whenever a thread exits to clean up thread specific data.
+ *
+ * This function is actually implemented in thread_value.c.
+ *
+ * @param value                value, as passed to thread_tls_put()
+ */
+void thread_tls_cleanup(void *value);
+
+#endif /** WINDOWS_THREAD_H_ @}*/
diff --git a/src/libstrongswan/threading/windows/thread_value.c b/src/libstrongswan/threading/windows/thread_value.c
new file mode 100644 (file)
index 0000000..8ba127f
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * 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 <library.h>
+#include <threading/thread_value.h>
+
+#include "thread.h"
+
+
+typedef struct private_thread_value_t private_thread_value_t;
+
+/**
+ * Unified thread_value_t implementation
+ */
+struct private_thread_value_t {
+
+       /**
+        * Public interface.
+        */
+       thread_value_t public;
+
+       union {
+
+               /**
+                * Cleanup function
+                */
+               thread_cleanup_t cleanup;
+
+               /**
+                * Windows TLS index, if used
+                */
+               DWORD index;
+       };
+};
+
+/**
+ * TLS entry
+ */
+typedef struct {
+       /** TLS value */
+       void *value;
+       /** cleanup handler function */
+       thread_cleanup_t cleanup;
+} entry_t;
+
+/**
+ * See windows/thread.h
+ */
+void thread_tls_cleanup(void *value)
+{
+       entry_t *entry = (entry_t*)value;
+
+       if (entry->cleanup)
+       {
+               entry->cleanup(entry->value);
+       }
+       free(entry);
+}
+
+METHOD(thread_value_t, tls_set, void,
+       private_thread_value_t *this, void *val)
+{
+       entry_t *entry;
+
+       if (val)
+       {
+               INIT(entry,
+                       .cleanup = this->cleanup,
+                       .value = val,
+               );
+
+               free(thread_tls_put(this, entry));
+       }
+       else
+       {
+               free(thread_tls_remove(this));
+       }
+}
+
+METHOD(thread_value_t, tls_get, void*,
+       private_thread_value_t *this)
+{
+       entry_t *entry;
+
+       entry = thread_tls_get(this);
+       if (entry)
+       {
+               return entry->value;
+       }
+       return NULL;
+}
+
+METHOD(thread_value_t, tls_destroy, void,
+       private_thread_value_t *this)
+{
+       entry_t *entry;
+
+       entry = thread_tls_remove(this);
+       if (entry)
+       {
+               if (entry->cleanup)
+               {
+                       entry->cleanup(entry->value);
+               }
+               free(entry);
+       }
+       free(this);
+}
+
+METHOD(thread_value_t, tls_set_index, void,
+       private_thread_value_t *this, void *val)
+{
+       TlsSetValue(this->index, val);
+}
+
+METHOD(thread_value_t, tls_get_index, void*,
+       private_thread_value_t *this)
+{
+       return TlsGetValue(this->index);
+}
+
+METHOD(thread_value_t, tls_destroy_index, void,
+       private_thread_value_t *this)
+{
+       TlsFree(this->index);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+thread_value_t *thread_value_create(thread_cleanup_t cleanup)
+{
+       private_thread_value_t *this;
+       DWORD index = TLS_OUT_OF_INDEXES;
+
+       /* we have two implementations: Windows Tls* functions do not support
+        * callbacks and has limited instances. We use it nonetheless if possible,
+        * especially as leak detective relies on TLS, but we have to mangle
+        * leak detective state for TLS storage. */
+
+       if (!cleanup)
+       {
+               index = TlsAlloc();
+       }
+
+       if (index == TLS_OUT_OF_INDEXES)
+       {
+               INIT(this,
+                       .public = {
+                               .set = _tls_set,
+                               .get = _tls_get,
+                               .destroy = _tls_destroy,
+                       },
+                       .cleanup = cleanup,
+               );
+       }
+       else
+       {
+               INIT(this,
+                       .public = {
+                               .set = _tls_set_index,
+                               .get = _tls_get_index,
+                               .destroy = _tls_destroy_index,
+                       },
+                       .index = index,
+               );
+       }
+
+       return &this->public;
+}