diff --git a/bake.cmd b/bake.cmd index 47dbac4..106c2af 100644 --- a/bake.cmd +++ b/bake.cmd @@ -29,23 +29,16 @@ mkdir bin mkdir bin\%PLATFORM% del ciabatta.lib 2> nul -for /R src\%PLATFORM% %%F in (*.c) do ( - echo %%F - clang -Isrc/win -c -o bin\%PLATFORM%\%%~nF.obj %%F %CIABATTA_OPTIONS% -) +call bake_cc.cmd for /R src\%PLATFORM% %%F in (*.asm) do ( echo %%F nasm %%F -f win64 -o bin\%PLATFORM%\%%~nF.obj ) -for /R src\code %%F in (*.c) do ( - echo %%F - clang -c -o bin\%%~nF.obj %%F %CIABATTA_OPTIONS% -) llvm-ar rc ciabatta.lib bin\*.obj bin\%PLATFORM%\*.obj :skip_crt_compilation -if "%TEST%"=="" set TEST=assert +if "%TEST%"=="" set TEST=threads echo Compiling test_%TEST%.c clang test\test_%TEST%.c ciabatta.lib -std=c11 -lDbghelp -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS% diff --git a/bake_cc.cmd b/bake_cc.cmd new file mode 100644 index 0000000..798f005 --- /dev/null +++ b/bake_cc.cmd @@ -0,0 +1,8 @@ +for /R src\%PLATFORM% %%F in (*.c) do ( + echo %%F + start /B clang -Isrc/win -c -o bin\%PLATFORM%\%%~nF.obj %%F %CIABATTA_OPTIONS% +) +for /R src\code %%F in (*.c) do ( + echo %%F + start /B clang -c -o bin\%%~nF.obj %%F %CIABATTA_OPTIONS% +) diff --git a/code/unicode/wctype.json b/code/unicode/wctype.json new file mode 100644 index 0000000..e69de29 diff --git a/src/win/win_threads.c b/src/win/win_threads.c index 1b89c7b..3944e05 100644 --- a/src/win/win_threads.c +++ b/src/win/win_threads.c @@ -6,10 +6,80 @@ // https://preshing.com/20120305/implementing-a-recursive-mutex/ // https://preshing.com/20120226/roll-your-own-lightweight-mutex/ #include +#include #include DWORD _tls_index = 0; +typedef struct thrd__wrapper_info { + thrd_start_t func; + void* arg; +} thrd__wrapper_info; + +static DWORD thrd__wrapper(void* arg) { + thrd__wrapper_info info = *((thrd__wrapper_info*) arg); + int result = info.func(info.arg); + + // TODO(NeGate): setup TSS dtors here + return (DWORD) result; +} + +thrd_t thrd_current(void) { + return (thrd_t){ GetCurrentThread() }; +} + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) { + thrd__wrapper_info* info = malloc(sizeof(thrd__wrapper_info)); + 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, thrd__wrapper, 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) { + if (WaitForSingleObject(thr.handle, INFINITE) == WAIT_FAILED) { + return thrd_error; + } + + 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) { + // TODO(NeGate): setup TSS dtors here + ExitThread((DWORD) res); +} + void mtx_destroy(mtx_t *mtx) { CloseHandle(mtx->semaphore); } @@ -30,9 +100,9 @@ int mtx_init(mtx_t *mtx, int type) { int mtx_lock(mtx_t *mtx) { bool try_recursive = (mtx->type == mtx_recursive); -#ifdef _DEBUG + #ifdef _DEBUG try_recursive = true; -#endif + #endif if (try_recursive) { DWORD tid = GetCurrentThreadId(); @@ -42,11 +112,11 @@ int mtx_lock(mtx_t *mtx) { WaitForSingleObject(mtx->semaphore, INFINITE); } else { // we recursive and already locked -#ifdef _DEBUG + #ifdef _DEBUG if (mtx->type != mtx_recursive) { return thrd_error; } -#endif + #endif } } @@ -67,20 +137,20 @@ int mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts) { int mtx_trylock(mtx_t *mtx) { bool try_recursive = (mtx->type == mtx_recursive); -#ifdef _DEBUG + #ifdef _DEBUG try_recursive = true; -#endif + #endif if (try_recursive) { DWORD tid = GetCurrentThreadId(); // Do we own this mutex on this thread already? if (mtx->owner == tid) { -#ifdef _DEBUG + #ifdef _DEBUG if (mtx->type != mtx_recursive) { return thrd_error; } -#endif + #endif atomic_fetch_add(&mtx->counter, 1); } else { @@ -106,9 +176,9 @@ int mtx_trylock(mtx_t *mtx) { int mtx_unlock(mtx_t *mtx) { bool try_recursive = (mtx->type == mtx_recursive); -#if _DEBUG + #if _DEBUG try_recursive = true; -#endif + #endif if (try_recursive) { DWORD tid = GetCurrentThreadId(); @@ -122,11 +192,11 @@ int mtx_unlock(mtx_t *mtx) { if (atomic_fetch_sub_explicit(&mtx->counter, 1, memory_order_release) > 0) { if (recur == 0) ReleaseSemaphore(mtx->semaphore, 1, NULL); else { -#ifdef _DEBUG + #ifdef _DEBUG if (mtx->type != mtx_recursive) { return thrd_error; } -#endif + #endif } } } else { diff --git a/test/test_threads.c b/test/test_threads.c new file mode 100644 index 0000000..fcec016 --- /dev/null +++ b/test/test_threads.c @@ -0,0 +1,27 @@ +#include +#include +#include + +atomic_int acnt; +int cnt; + +int f(void* thr_data) +{ + for(int n = 0; n < 1000; ++n) { + atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed); // atomic + ++cnt; // undefined behavior, in practice some updates missed + } + return 0; +} + +int main(void) +{ + thrd_t thr[10]; + for(int n = 0; n < 10; ++n) + thrd_create(&thr[n], f, NULL); + for(int n = 0; n < 10; ++n) + thrd_join(thr[n], NULL); + + printf("The atomic counter is %d\n", acnt); + printf("The non-atomic counter is %d\n", cnt); +}