#include <time.h>
#include <synchapi.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include "os_thread.h"
#include "util.h"
#include "out.h"
typedef struct {
unsigned attr;
CRITICAL_SECTION lock;
} internal_os_mutex_t;
typedef struct {
unsigned attr;
char is_write;
SRWLOCK lock;
} internal_os_rwlock_t;
typedef struct {
unsigned attr;
CONDITION_VARIABLE cond;
} internal_os_cond_t;
typedef long long internal_os_once_t;
typedef struct {
HANDLE handle;
} internal_semaphore_t;
typedef struct {
GROUP_AFFINITY affinity;
} internal_os_cpu_set_t;
typedef struct {
HANDLE thread_handle;
void *arg;
void *(*start_routine)(void *);
void *result;
} internal_os_thread_t;
#define DELTA_WIN2UNIX (11644473600000000ull)
#define TIMED_LOCK(action, ts) {\
if ((action) == TRUE)\
return 0;\
unsigned long long et = (ts)->tv_sec * 1000000 + (ts)->tv_nsec / 1000;\
while (1) {\
FILETIME _t;\
GetSystemTimeAsFileTime(&_t);\
ULARGE_INTEGER _UI = {\
.HighPart = _t.dwHighDateTime,\
.LowPart = _t.dwLowDateTime,\
};\
if (_UI.QuadPart / 10 - DELTA_WIN2UNIX >= et)\
return ETIMEDOUT;\
if ((action) == TRUE)\
return 0;\
Sleep(1);\
}\
return ETIMEDOUT;\
}
int
os_mutex_init(os_mutex_t *__restrict mutex)
{
COMPILE_ERROR_ON(sizeof(os_mutex_t) < sizeof(internal_os_mutex_t));
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
InitializeCriticalSection(&mutex_internal->lock);
return 0;
}
int
os_mutex_destroy(os_mutex_t *__restrict mutex)
{
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
DeleteCriticalSection(&mutex_internal->lock);
return 0;
}
_Use_decl_annotations_
int
os_mutex_lock(os_mutex_t *__restrict mutex)
{
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
EnterCriticalSection(&mutex_internal->lock);
if (mutex_internal->lock.RecursionCount > 1) {
LeaveCriticalSection(&mutex_internal->lock);
FATAL("deadlock detected");
}
return 0;
}
_Use_decl_annotations_
int
os_mutex_trylock(os_mutex_t *__restrict mutex)
{
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
if (TryEnterCriticalSection(&mutex_internal->lock) == FALSE)
return EBUSY;
if (mutex_internal->lock.RecursionCount > 1) {
LeaveCriticalSection(&mutex_internal->lock);
return EBUSY;
}
return 0;
}
int
os_mutex_timedlock(os_mutex_t *__restrict mutex,
const struct timespec *abstime)
{
TIMED_LOCK((os_mutex_trylock(mutex) == 0), abstime);
}
int
os_mutex_unlock(os_mutex_t *__restrict mutex)
{
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
LeaveCriticalSection(&mutex_internal->lock);
return 0;
}
int
os_rwlock_init(os_rwlock_t *__restrict rwlock)
{
COMPILE_ERROR_ON(sizeof(os_rwlock_t) < sizeof(internal_os_rwlock_t));
internal_os_rwlock_t *rwlock_internal = (internal_os_rwlock_t *)rwlock;
InitializeSRWLock(&rwlock_internal->lock);
return 0;
}
int
os_rwlock_destroy(os_rwlock_t *__restrict rwlock)
{
UNREFERENCED_PARAMETER(rwlock);
return 0;
}
int
os_rwlock_rdlock(os_rwlock_t *__restrict rwlock)
{
internal_os_rwlock_t *rwlock_internal = (internal_os_rwlock_t *)rwlock;
AcquireSRWLockShared(&rwlock_internal->lock);
rwlock_internal->is_write = 0;
return 0;
}
int
os_rwlock_wrlock(os_rwlock_t *__restrict rwlock)
{
internal_os_rwlock_t *rwlock_internal = (internal_os_rwlock_t *)rwlock;
AcquireSRWLockExclusive(&rwlock_internal->lock);
rwlock_internal->is_write = 1;
return 0;
}
int
os_rwlock_tryrdlock(os_rwlock_t *__restrict rwlock)
{
internal_os_rwlock_t *rwlock_internal = (internal_os_rwlock_t *)rwlock;
if (TryAcquireSRWLockShared(&rwlock_internal->lock) == FALSE) {
return EBUSY;
} else {
rwlock_internal->is_write = 0;
return 0;
}
}
_Use_decl_annotations_
int
os_rwlock_trywrlock(os_rwlock_t *__restrict rwlock)
{
internal_os_rwlock_t *rwlock_internal = (internal_os_rwlock_t *)rwlock;
if (TryAcquireSRWLockExclusive(&rwlock_internal->lock) == FALSE) {
return EBUSY;
} else {
rwlock_internal->is_write = 1;
return 0;
}
}
int
os_rwlock_timedrdlock(os_rwlock_t *__restrict rwlock,
const struct timespec *abstime)
{
TIMED_LOCK((os_rwlock_tryrdlock(rwlock) == 0), abstime);
}
int
os_rwlock_timedwrlock(os_rwlock_t *__restrict rwlock,
const struct timespec *abstime)
{
TIMED_LOCK((os_rwlock_trywrlock(rwlock) == 0), abstime);
}
_Use_decl_annotations_
int
os_rwlock_unlock(os_rwlock_t *__restrict rwlock)
{
internal_os_rwlock_t *rwlock_internal = (internal_os_rwlock_t *)rwlock;
if (rwlock_internal->is_write)
ReleaseSRWLockExclusive(&rwlock_internal->lock);
else
ReleaseSRWLockShared(&rwlock_internal->lock);
return 0;
}
int
os_cond_init(os_cond_t *__restrict cond)
{
COMPILE_ERROR_ON(sizeof(os_cond_t) < sizeof(internal_os_cond_t));
internal_os_cond_t *cond_internal = (internal_os_cond_t *)cond;
InitializeConditionVariable(&cond_internal->cond);
return 0;
}
int
os_cond_destroy(os_cond_t *__restrict cond)
{
UNREFERENCED_PARAMETER(cond);
return 0;
}
int
os_cond_broadcast(os_cond_t *__restrict cond)
{
internal_os_cond_t *cond_internal = (internal_os_cond_t *)cond;
WakeAllConditionVariable(&cond_internal->cond);
return 0;
}
int
os_cond_signal(os_cond_t *__restrict cond)
{
internal_os_cond_t *cond_internal = (internal_os_cond_t *)cond;
WakeConditionVariable(&cond_internal->cond);
return 0;
}
static DWORD
get_rel_wait(const struct timespec *abstime)
{
struct __timeb64 t;
_ftime64_s(&t);
time_t now_ms = t.time * 1000 + t.millitm;
time_t ms = (time_t)(abstime->tv_sec * 1000 +
abstime->tv_nsec / 1000000);
DWORD rel_wait = (DWORD)(ms - now_ms);
return rel_wait < 0 ? 0 : rel_wait;
}
int
os_cond_timedwait(os_cond_t *__restrict cond,
os_mutex_t *__restrict mutex, const struct timespec *abstime)
{
internal_os_cond_t *cond_internal = (internal_os_cond_t *)cond;
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
BOOL ret;
SetLastError(0);
ret = SleepConditionVariableCS(&cond_internal->cond,
&mutex_internal->lock, get_rel_wait(abstime));
if (ret == FALSE)
return (GetLastError() == ERROR_TIMEOUT) ? ETIMEDOUT : EINVAL;
return 0;
}
int
os_cond_wait(os_cond_t *__restrict cond,
os_mutex_t *__restrict mutex)
{
internal_os_cond_t *cond_internal = (internal_os_cond_t *)cond;
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
BOOL ret;
ret = SleepConditionVariableCS(&cond_internal->cond,
&mutex_internal->lock, INFINITE);
return (ret == FALSE) ? EINVAL : 0;
}
int
os_once(os_once_t *once, void (*func)(void))
{
internal_os_once_t *once_internal = (internal_os_once_t *)once;
internal_os_once_t tmp;
while ((tmp = *once_internal) != 2) {
if (tmp == 1)
continue;
if (!util_bool_compare_and_swap64(once_internal, tmp, 1))
continue;
func();
if (!util_bool_compare_and_swap64(once_internal, 1, 2)) {
ERR("error setting once");
return -1;
}
}
return 0;
}
int
os_tls_key_create(os_tls_key_t *key, void (*destructor)(void *))
{
*key = FlsAlloc(destructor);
if (*key == TLS_OUT_OF_INDEXES)
return EAGAIN;
return 0;
}
int
os_tls_key_delete(os_tls_key_t key)
{
if (!FlsFree(key))
return EINVAL;
return 0;
}
int
os_tls_set(os_tls_key_t key, const void *value)
{
if (!FlsSetValue(key, (LPVOID)value))
return ENOENT;
return 0;
}
void *
os_tls_get(os_tls_key_t key)
{
return FlsGetValue(key);
}
static unsigned __stdcall
os_thread_start_routine_wrapper(void *arg)
{
internal_os_thread_t *thread_info = (internal_os_thread_t *)arg;
thread_info->result = thread_info->start_routine(thread_info->arg);
return 0;
}
int
os_thread_create(os_thread_t *thread, const os_thread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
COMPILE_ERROR_ON(sizeof(os_thread_t) < sizeof(internal_os_thread_t));
internal_os_thread_t *thread_info = (internal_os_thread_t *)thread;
thread_info->start_routine = start_routine;
thread_info->arg = arg;
thread_info->thread_handle = (HANDLE)_beginthreadex(NULL, 0,
os_thread_start_routine_wrapper, thread_info, CREATE_SUSPENDED,
NULL);
if (thread_info->thread_handle == 0) {
free(thread_info);
return errno;
}
if (ResumeThread(thread_info->thread_handle) == -1) {
free(thread_info);
return EAGAIN;
}
return 0;
}
int
os_thread_join(os_thread_t *thread, void **result)
{
internal_os_thread_t *internal_thread = (internal_os_thread_t *)thread;
WaitForSingleObject(internal_thread->thread_handle, INFINITE);
CloseHandle(internal_thread->thread_handle);
if (result != NULL)
*result = internal_thread->result;
return 0;
}
void
os_thread_self(os_thread_t *thread)
{
internal_os_thread_t *internal_thread = (internal_os_thread_t *)thread;
internal_thread->thread_handle = GetCurrentThread();
}
void
os_cpu_zero(os_cpu_set_t *set)
{
internal_os_cpu_set_t *internal_set = (internal_os_cpu_set_t *)set;
memset(&internal_set->affinity, 0, sizeof(internal_set->affinity));
}
void
os_cpu_set(size_t cpu, os_cpu_set_t *set)
{
internal_os_cpu_set_t *internal_set = (internal_os_cpu_set_t *)set;
int sum = 0;
int group_max = GetActiveProcessorGroupCount();
int group = 0;
while (group < group_max) {
sum += GetActiveProcessorCount(group);
if (sum > cpu) {
if (internal_set->affinity.Group != group) {
internal_set->affinity.Mask = 0;
internal_set->affinity.Group = group;
}
cpu -= sum - GetActiveProcessorCount(group);
internal_set->affinity.Mask |= 1LL << cpu;
return;
}
group++;
}
FATAL("os_cpu_set cpu out of bounds");
}
int
os_thread_setaffinity_np(os_thread_t *thread, size_t set_size,
const os_cpu_set_t *set)
{
internal_os_cpu_set_t *internal_set = (internal_os_cpu_set_t *)set;
internal_os_thread_t *internal_thread = (internal_os_thread_t *)thread;
int ret = SetThreadGroupAffinity(internal_thread->thread_handle,
&internal_set->affinity, NULL);
return ret != 0 ? 0 : EINVAL;
}
int
os_semaphore_init(os_semaphore_t *sem, unsigned value)
{
internal_semaphore_t *internal_sem = (internal_semaphore_t *)sem;
internal_sem->handle = CreateSemaphore(NULL,
value, LONG_MAX, NULL);
return internal_sem->handle != 0 ? 0 : -1;
}
int
os_semaphore_destroy(os_semaphore_t *sem)
{
internal_semaphore_t *internal_sem = (internal_semaphore_t *)sem;
BOOL ret = CloseHandle(internal_sem->handle);
return ret ? 0 : -1;
}
int
os_semaphore_wait(os_semaphore_t *sem)
{
internal_semaphore_t *internal_sem = (internal_semaphore_t *)sem;
DWORD ret = WaitForSingleObject(internal_sem->handle, INFINITE);
return ret == WAIT_OBJECT_0 ? 0 : -1;
}
int
os_semaphore_trywait(os_semaphore_t *sem)
{
internal_semaphore_t *internal_sem = (internal_semaphore_t *)sem;
DWORD ret = WaitForSingleObject(internal_sem->handle, 0);
if (ret == WAIT_TIMEOUT)
errno = EAGAIN;
return ret == WAIT_OBJECT_0 ? 0 : -1;
}
int
os_semaphore_post(os_semaphore_t *sem)
{
internal_semaphore_t *internal_sem = (internal_semaphore_t *)sem;
BOOL ret = ReleaseSemaphore(internal_sem->handle, 1, NULL);
return ret ? 0 : -1;
}