mirror of https://github.com/flysand7/ciabatta.git
363 lines
8.7 KiB
C
363 lines
8.7 KiB
C
|
|
// Note(bumbread):
|
|
// https://gist.github.com/wbenny/6d7fc92e9b5c3194ce56bf8c60d6191d
|
|
|
|
#pragma comment(linker, "/merge:.CRT=.rdata")
|
|
|
|
#pragma section(".CRT$XLA", read)
|
|
__declspec(allocate(".CRT$XLA")) const PIMAGE_TLS_CALLBACK __xl_a = NULL;
|
|
|
|
#pragma section(".CRT$XLZ", read)
|
|
__declspec(allocate(".CRT$XLZ")) const PIMAGE_TLS_CALLBACK __xl_z = NULL;
|
|
|
|
#pragma section(".CRT$XLM", read)
|
|
__declspec(allocate(".CRT$XLM")) extern const PIMAGE_TLS_CALLBACK TlsCallbackArray;
|
|
|
|
char _tls_start = 0;
|
|
char _tls_end = 0;
|
|
unsigned int _tls_index = 0;
|
|
|
|
const IMAGE_TLS_DIRECTORY _tls_used = {
|
|
(ULONG_PTR)&_tls_start,
|
|
(ULONG_PTR)&_tls_end,
|
|
(ULONG_PTR)&_tls_index,
|
|
(ULONG_PTR)(&__xl_a + 1),
|
|
};
|
|
|
|
static void _thread_cleanup();
|
|
|
|
VOID NTAPI _tls_callback(
|
|
PVOID DllHandle,
|
|
DWORD Reason,
|
|
PVOID Reserved
|
|
)
|
|
{
|
|
switch(Reason) {
|
|
case DLL_THREAD_ATTACH: break;
|
|
case DLL_THREAD_DETACH: {
|
|
_thread_cleanup();
|
|
} break;
|
|
case DLL_PROCESS_ATTACH: break;
|
|
case DLL_PROCESS_DETACH: break;
|
|
}
|
|
// __debugbreak();
|
|
}
|
|
|
|
const PIMAGE_TLS_CALLBACK TlsCallbackArray = { &_tls_callback };
|
|
|
|
// NOTE: debug mutexes will follow the recursive logic but error if they
|
|
// actually recurse, this is slower than doing plain logic but it helps
|
|
// debug weird mutex errors.
|
|
//
|
|
// Based on these posts:
|
|
// https://preshing.com/20120305/implementing-a-recursive-mutex/
|
|
// https://preshing.com/20120226/roll-your-own-lightweight-mutex/
|
|
|
|
typedef struct UserClosure {
|
|
thrd_start_t func;
|
|
void* arg;
|
|
} UserClosure;
|
|
|
|
static DWORD _thread_call_user(void* arg) {
|
|
UserClosure info = *((UserClosure*) arg);
|
|
int result = info.func(info.arg);
|
|
free(arg);
|
|
return (DWORD) result;
|
|
}
|
|
|
|
thrd_t thrd_current(void) {
|
|
return (thrd_t){ GetCurrentThread() };
|
|
}
|
|
|
|
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
|
|
UserClosure* info = malloc(sizeof(UserClosure));
|
|
if (info == NULL) {
|
|
return thrd_nomem;
|
|
}
|
|
|
|
info->func = func;
|
|
info->arg = arg;
|
|
|
|
// technically thrd_start_t and LPTHREAD_START_ROUTINE aren't the same
|
|
// but are close enough to be ABI compatible, namely a difference in
|
|
// signedness of the return val.
|
|
thr->handle = CreateThread(NULL, 0, _thread_call_user, info, 0, NULL);
|
|
return thr->handle != NULL ? thrd_success : thrd_error;
|
|
}
|
|
|
|
int thrd_detach(thrd_t thr) {
|
|
return CloseHandle(thr.handle) != 0 ? thrd_success : thrd_error;
|
|
}
|
|
|
|
int thrd_equal(thrd_t thr0, thrd_t thr1) {
|
|
return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
|
|
}
|
|
|
|
int thrd_join(thrd_t thr, int *res) {
|
|
DWORD wait = WaitForSingleObject(thr.handle, INFINITE);
|
|
if (wait == WAIT_FAILED) {
|
|
return thrd_error;
|
|
} else if (wait == WAIT_TIMEOUT) {
|
|
return thrd_timedout;
|
|
}
|
|
|
|
if (res != NULL) {
|
|
// snatch that exit code
|
|
DWORD ures;
|
|
if (GetExitCodeThread(thr.handle, &ures) == 0) {
|
|
CloseHandle(thr.handle);
|
|
return thrd_error;
|
|
}
|
|
|
|
*res = (int) ures;
|
|
}
|
|
|
|
CloseHandle(thr.handle);
|
|
return thrd_success;
|
|
}
|
|
|
|
void thrd_yield(void) {
|
|
Sleep(0);
|
|
}
|
|
|
|
_Noreturn void thrd_exit(int res) {
|
|
_thread_cleanup();
|
|
ExitThread((DWORD)res);
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
// TSS functions
|
|
|
|
#define TSS_KEYS_MAX 1088
|
|
|
|
static tss_dtor_t _tss_dtors[TSS_KEYS_MAX];
|
|
static bool _tss_init[TSS_KEYS_MAX];
|
|
|
|
static void _thread_cleanup() {
|
|
for(int i = 0; i != TSS_DTOR_ITERATIONS; ++i) {
|
|
for(unsigned k = 1; k != TSS_KEYS_MAX; ++k) {
|
|
if(!_tss_init[k]) {
|
|
continue;
|
|
}
|
|
void *data = TlsGetValue(k);
|
|
if(data == NULL) {
|
|
continue;
|
|
}
|
|
TlsSetValue(k, NULL);
|
|
if(_tss_dtors[k]) {
|
|
_tss_dtors[k](data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int tss_create(tss_t *key, tss_dtor_t dtor) {
|
|
DWORD tls_index = TlsAlloc();
|
|
if(tls_index == TLS_OUT_OF_INDEXES) {
|
|
return thrd_error;
|
|
}
|
|
key->tls_index = tls_index;
|
|
if(tls_index >= TSS_KEYS_MAX) {
|
|
__debugbreak();
|
|
TlsFree(tls_index);
|
|
return thrd_error;
|
|
}
|
|
_tss_init[tls_index] = true;
|
|
_tss_dtors[tls_index] = dtor;
|
|
return thrd_success;
|
|
}
|
|
|
|
void tss_delete(tss_t key) {
|
|
_tss_init[key.tls_index] = false;
|
|
_tss_dtors[key.tls_index] = NULL;
|
|
TlsFree(key.tls_index);
|
|
}
|
|
|
|
void *tss_get(tss_t key) {
|
|
void *data = TlsGetValue(key.tls_index);
|
|
if(data == NULL && GetLastError() != ERROR_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
int tss_set(tss_t key, void *val) {
|
|
if(!TlsSetValue(key.tls_index, val)) {
|
|
return thrd_error;
|
|
}
|
|
return thrd_success;
|
|
}
|
|
|
|
// Call once
|
|
|
|
static BOOL _call_once_trampoline(PINIT_ONCE init_once, PVOID param, PVOID *ctx) {
|
|
void (*user_func)(void) = param;
|
|
user_func();
|
|
return TRUE;
|
|
}
|
|
|
|
void call_once(once_flag *flag, void (*func)(void)) {
|
|
void *funcp = func;
|
|
InitOnceExecuteOnce((void *)flag, _call_once_trampoline, funcp, NULL);
|
|
}
|
|
|
|
// Condition variables
|
|
|
|
int cnd_init(cnd_t *cond) {
|
|
InitializeConditionVariable((void *)cond);
|
|
return thrd_success;
|
|
}
|
|
|
|
int cnd_broadcast(cnd_t *cond) {
|
|
WakeAllConditionVariable((void *)cond);
|
|
return thrd_success;
|
|
}
|
|
|
|
void cnd_destroy(cnd_t *cond) {
|
|
return; // Does nothing
|
|
}
|
|
|
|
int cnd_signal(cnd_t *cond) {
|
|
WakeConditionVariable((void *)cond);
|
|
return thrd_success;
|
|
}
|
|
|
|
int cnd_wait(cnd_t *cond, mtx_t *mtx) {
|
|
return thrd_error; // TODO after mutexes
|
|
}
|
|
|
|
int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx, const struct timespec *restrict ts) {
|
|
return thrd_error; // TODO after mutexes
|
|
}
|
|
|
|
// Mutex functions
|
|
|
|
void mtx_destroy(mtx_t *mtx) {
|
|
CloseHandle(mtx->semaphore);
|
|
}
|
|
|
|
int mtx_init(mtx_t *mtx, int type) {
|
|
*mtx = (mtx_t){
|
|
.type = type,
|
|
.semaphore = CreateSemaphore(NULL, 0, 1, NULL)
|
|
};
|
|
|
|
if (type == mtx_timed) {
|
|
// TODO(NeGate): implement timed mutexes
|
|
return thrd_error;
|
|
}
|
|
|
|
return thrd_success;
|
|
}
|
|
|
|
int mtx_lock(mtx_t *mtx) {
|
|
bool try_recursive = (mtx->type == mtx_recursive);
|
|
#ifdef _DEBUG
|
|
try_recursive = true;
|
|
#endif
|
|
|
|
if (try_recursive) {
|
|
DWORD tid = GetCurrentThreadId();
|
|
|
|
if (atomic_fetch_add_explicit(&mtx->counter, 1, memory_order_acquire) > 1) {
|
|
if (tid != mtx->owner) {
|
|
WaitForSingleObject(mtx->semaphore, INFINITE);
|
|
} else {
|
|
// we recursive and already locked
|
|
#ifdef _DEBUG
|
|
if (mtx->type != mtx_recursive) {
|
|
return thrd_error;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
mtx->owner = tid;
|
|
mtx->recursion++;
|
|
} else {
|
|
if (atomic_fetch_add_explicit(&mtx->counter, 1, memory_order_acquire) > 1) {
|
|
WaitForSingleObject(mtx->semaphore, INFINITE);
|
|
}
|
|
}
|
|
|
|
return thrd_success;
|
|
}
|
|
|
|
int mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts) {
|
|
return thrd_error;
|
|
}
|
|
|
|
int mtx_trylock(mtx_t *mtx) {
|
|
bool try_recursive = (mtx->type == mtx_recursive);
|
|
#ifdef _DEBUG
|
|
try_recursive = true;
|
|
#endif
|
|
|
|
if (try_recursive) {
|
|
DWORD tid = GetCurrentThreadId();
|
|
|
|
// Do we own this mutex on this thread already?
|
|
if (mtx->owner == tid) {
|
|
#ifdef _DEBUG
|
|
if (mtx->type != mtx_recursive) {
|
|
return thrd_error;
|
|
}
|
|
#endif
|
|
|
|
atomic_fetch_add(&mtx->counter, 1);
|
|
} else {
|
|
int expected = 1;
|
|
if (!atomic_compare_exchange_strong(&mtx->counter, &expected, 0)) {
|
|
return thrd_busy;
|
|
}
|
|
|
|
mtx->owner = tid;
|
|
}
|
|
|
|
mtx->recursion++;
|
|
return thrd_success;
|
|
} else {
|
|
int expected = 1;
|
|
if (!atomic_compare_exchange_strong(&mtx->counter, &expected, 0)) {
|
|
return thrd_busy;
|
|
}
|
|
|
|
return thrd_success;
|
|
}
|
|
}
|
|
|
|
int mtx_unlock(mtx_t *mtx) {
|
|
bool try_recursive = (mtx->type == mtx_recursive);
|
|
#if _DEBUG
|
|
try_recursive = true;
|
|
#endif
|
|
|
|
if (try_recursive) {
|
|
DWORD tid = GetCurrentThreadId();
|
|
if (tid != mtx->owner) return thrd_error;
|
|
|
|
unsigned long recur = --mtx->recursion;
|
|
if (recur == 0) {
|
|
mtx->owner = 0;
|
|
}
|
|
|
|
if (atomic_fetch_sub_explicit(&mtx->counter, 1, memory_order_release) > 0) {
|
|
if (recur == 0) ReleaseSemaphore(mtx->semaphore, 1, NULL);
|
|
else {
|
|
#ifdef _DEBUG
|
|
if (mtx->type != mtx_recursive) {
|
|
return thrd_error;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
} else {
|
|
// release?
|
|
if (atomic_fetch_sub_explicit(&mtx->counter, 1, memory_order_release) > 0) {
|
|
ReleaseSemaphore(mtx->semaphore, 1, NULL);
|
|
}
|
|
}
|
|
|
|
return thrd_success;
|
|
}
|