windows: Provide a complete native Windows threading backend
This commit is contained in:
@@ -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 \
|
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
|
||||||
selectors/traffic_selector.c settings/settings.c settings/settings_types.c \
|
selectors/traffic_selector.c settings/settings.c settings/settings_types.c \
|
||||||
settings/settings_parser.y settings/settings_lexer.l \
|
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/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/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
|
||||||
utils/parser_helper.c utils/test.c utils/utils/strerror.c
|
utils/parser_helper.c utils/test.c utils/utils/strerror.c
|
||||||
|
|
||||||
if !USE_WINDOWS
|
if !USE_WINDOWS
|
||||||
libstrongswan_la_SOURCES += \
|
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_unix.c \
|
||||||
networking/streams/stream_service_unix.c
|
networking/streams/stream_service_unix.c
|
||||||
endif
|
endif
|
||||||
@@ -89,7 +93,8 @@ resolver/rr.h resolver/resolver_manager.h \
|
|||||||
plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.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/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
|
||||||
processing/scheduler.h processing/watcher.h selectors/traffic_selector.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/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
|
||||||
threading/rwlock.h threading/rwlock_condvar.h threading/lock_profiler.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 \
|
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
|
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 = \
|
AM_CPPFLAGS = \
|
||||||
-I$(top_srcdir)/src/libstrongswan \
|
-I$(top_srcdir)/src/libstrongswan \
|
||||||
@@ -122,7 +127,15 @@ AM_YFLAGS = -v -d
|
|||||||
if USE_WINDOWS
|
if USE_WINDOWS
|
||||||
libstrongswan_la_LIBADD += -lws2_32
|
libstrongswan_la_LIBADD += -lws2_32
|
||||||
libstrongswan_la_SOURCES += \
|
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
|
utils/windows.c
|
||||||
|
else
|
||||||
|
libstrongswan_la_LIBADD += $(PTHREADLIB)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if USE_DBGHELP
|
if USE_DBGHELP
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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_ @}*/
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user