diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
commit | 81c63b0eed39432878f78727f60a1e7499645199 (patch) | |
tree | 82387d8fecd1c20788fd8bd784a9b0bde091fb6b /src/libstrongswan/threading/windows | |
parent | c5ebfc7b9c16551fe825dc1d79c3f7e2f096f6c9 (diff) | |
download | vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.tar.gz vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.zip |
Imported Upstream version 5.2.0
Diffstat (limited to 'src/libstrongswan/threading/windows')
-rw-r--r-- | src/libstrongswan/threading/windows/mutex.c | 196 | ||||
-rw-r--r-- | src/libstrongswan/threading/windows/rwlock.c | 220 | ||||
-rw-r--r-- | src/libstrongswan/threading/windows/semaphore.c | 101 | ||||
-rw-r--r-- | src/libstrongswan/threading/windows/spinlock.c | 79 | ||||
-rw-r--r-- | src/libstrongswan/threading/windows/thread.c | 677 | ||||
-rw-r--r-- | src/libstrongswan/threading/windows/thread.h | 74 | ||||
-rw-r--r-- | src/libstrongswan/threading/windows/thread_value.c | 178 |
7 files changed, 1525 insertions, 0 deletions
diff --git a/src/libstrongswan/threading/windows/mutex.c b/src/libstrongswan/threading/windows/mutex.c new file mode 100644 index 000000000..a26889580 --- /dev/null +++ b/src/libstrongswan/threading/windows/mutex.c @@ -0,0 +1,196 @@ +/* + * 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/mutex.h> +#include <threading/condvar.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 index 000000000..0de57f713 --- /dev/null +++ b/src/libstrongswan/threading/windows/rwlock.c @@ -0,0 +1,220 @@ +/* + * 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/rwlock.h> +#include <threading/rwlock_condvar.h> +#include <threading/thread_value.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 index 000000000..29f523d3e --- /dev/null +++ b/src/libstrongswan/threading/windows/semaphore.c @@ -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 index 000000000..155dd56dc --- /dev/null +++ b/src/libstrongswan/threading/windows/spinlock.c @@ -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 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); +} diff --git a/src/libstrongswan/threading/windows/thread.h b/src/libstrongswan/threading/windows/thread.h new file mode 100644 index 000000000..3f08c623b --- /dev/null +++ b/src/libstrongswan/threading/windows/thread.h @@ -0,0 +1,74 @@ +/* + * 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_ + +/* for conditionVariables, Vista */ +#define _WIN32_WINNT 0x0600 +#include <library.h> + +/** + * @defgroup windowsthread windows + * @ingroup threading + * + * @defgroup threadwindows thread + * @{ @ingroup windowsthread + */ + +/** + * Set active condvar of a thread before waiting in it. + * + * @param condvar 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 + * @return 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 index 000000000..d7bd7e64c --- /dev/null +++ b/src/libstrongswan/threading/windows/thread_value.c @@ -0,0 +1,178 @@ +/* + * 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 <threading/thread_value.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) + { + thread_tls_cleanup(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; +} |