mirror of
https://github.com/skeeto/w64devkit.git
synced 2026-10-23 06:08:56 +03:00
@@ -422,6 +422,12 @@ RUN /dl/mingw/mingw-w64-crt/configure \
|
||||
&& make -j$(nproc) \
|
||||
&& make install
|
||||
|
||||
COPY src/threads.c $PREFIX/src/
|
||||
COPY src/threads.h $PREFIX/include/
|
||||
RUN $ARCH-gcc -c -Oz -I$PREFIX/include/ \
|
||||
-ffunction-sections -Wa,--no-pad-sections $PREFIX/src/threads.c \
|
||||
&& $ARCH-ar r $PREFIX/lib/libmingwex.a threads.o
|
||||
|
||||
WORKDIR /winpthreads
|
||||
RUN /dl/mingw/mingw-w64-libraries/winpthreads/configure \
|
||||
--prefix=$PREFIX \
|
||||
|
||||
10
README.md
10
README.md
@@ -110,6 +110,16 @@ then the hint is zeroed: "no data." Eliminating this random data makes
|
||||
binaries more compressible and *theoretically* faster loading. See also:
|
||||
`peports`.
|
||||
|
||||
## C11 threading runtime
|
||||
|
||||
The toolchain includes the C standard header `threads.h`, and the runtime
|
||||
contains a fast, lean standard threads implementation. It is independent
|
||||
of pthreads and involves no extra linker flags. It supports only Vista and
|
||||
later, does not implement recursive or timed locks, and just like MSVC,
|
||||
`thrd_current` returns a dummy handle only useful with `thrd_equal`. Using
|
||||
this handle to join or detach will trap. Also like MSVC, `mtx_t` and
|
||||
`cnd_t` support zero-initialization and their destructors are no-ops.
|
||||
|
||||
## Recommended downloadable, offline documentation
|
||||
|
||||
With a few exceptions, such as Vim's built-in documentation (`:help`),
|
||||
|
||||
257
src/threads.c
Normal file
257
src/threads.c
Normal file
@@ -0,0 +1,257 @@
|
||||
// C11 threads implementation for w64devkit
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
#include <threads.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef __UINTPTR_TYPE__ uz;
|
||||
typedef __INT32_TYPE__ i32;
|
||||
typedef __INT64_TYPE__ i64;
|
||||
typedef __UINT64_TYPE__ u64;
|
||||
|
||||
#define W32 [[gnu::dllimport, gnu::stdcall]]
|
||||
W32 void AcquireSRWLockExclusive(uz *);
|
||||
W32 i32 CloseHandle(uz);
|
||||
W32 uz CreateThread(uz, uz, uz, uz, i32, uz);
|
||||
W32 void ExitThread[[noreturn]](i32);
|
||||
W32 i32 FlsAlloc(uz);
|
||||
W32 i32 FlsFree(i32);
|
||||
W32 uz FlsGetValue(i32);
|
||||
W32 i32 FlsSetValue(i32, uz);
|
||||
W32 i32 GetCurrentThreadId();
|
||||
W32 i32 GetExitCodeThread(uz, i32 *);
|
||||
W32 void GetSystemTimeAsFileTime(u64 *);
|
||||
W32 i32 InitOnceExecuteOnce(uz *, uz, uz, uz *);
|
||||
W32 void ReleaseSRWLockExclusive(uz *);
|
||||
W32 void Sleep(i32);
|
||||
W32 i32 SleepConditionVariableSRW(uz *, uz *, i32, i32);
|
||||
W32 i32 SwitchToThread();
|
||||
W32 i32 TryAcquireSRWLockExclusive(uz *);
|
||||
W32 i32 WaitForSingleObject(uz, i32);
|
||||
W32 void WakeAllConditionVariable(uz *);
|
||||
W32 void WakeConditionVariable(uz *);
|
||||
|
||||
typedef struct {
|
||||
thrd_start_t fn;
|
||||
void *arg;
|
||||
uz srw;
|
||||
uz cnd;
|
||||
i32 consumed;
|
||||
} Spawn;
|
||||
|
||||
[[gnu::stdcall]]
|
||||
static i32 thread_trampoline(void *p)
|
||||
{
|
||||
Spawn *sp = p;
|
||||
thrd_start_t fn = sp->fn; // fn/arg were written before CreateThread,
|
||||
void *arg = sp->arg; // so they're visible without the lock.
|
||||
AcquireSRWLockExclusive(&sp->srw);
|
||||
sp->consumed = 1;
|
||||
WakeConditionVariable(&sp->cnd);
|
||||
ReleaseSRWLockExclusive(&sp->srw);
|
||||
return fn(arg);
|
||||
}
|
||||
|
||||
int thrd_create(thrd_t *thr, thrd_start_t fn, void *arg)
|
||||
{
|
||||
Spawn sp = {.fn = fn, .arg = arg};
|
||||
i32 tid = 0;
|
||||
uz h = CreateThread(0, 0, (uz)&thread_trampoline, (uz)&sp, 0, (uz)&tid);
|
||||
if (!h) return thrd_error;
|
||||
AcquireSRWLockExclusive(&sp.srw);
|
||||
while (!sp.consumed) {
|
||||
SleepConditionVariableSRW(&sp.cnd, &sp.srw, -1, 0);
|
||||
}
|
||||
ReleaseSRWLockExclusive(&sp.srw);
|
||||
|
||||
thr->__handle = (i32)h;
|
||||
thr->__tid = tid;
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int thrd_equal(thrd_t a, thrd_t b)
|
||||
{
|
||||
return a.__tid == b.__tid;
|
||||
}
|
||||
|
||||
thrd_t thrd_current()
|
||||
{
|
||||
// The returned thrd_t can be passed to thrd_equal but not to
|
||||
// thrd_join or thrd_detach (which trap on a null handle). These
|
||||
// semantics match MSVC.
|
||||
return (thrd_t){0, GetCurrentThreadId()};
|
||||
}
|
||||
|
||||
int thrd_sleep(const struct timespec *d, struct timespec *r)
|
||||
{
|
||||
// Convert to milliseconds with overflow detection. If the duration
|
||||
// doesn't fit in i32 (or the math overflows), degrade to an infinite
|
||||
// sleep — no real program will run long enough to notice the difference.
|
||||
i64 ms;
|
||||
i32 timeout = -1;
|
||||
if (!__builtin_mul_overflow((i64)d->tv_sec, (i64)1000, &ms) &&
|
||||
!__builtin_add_overflow(ms, (i64)((d->tv_nsec + 999999)/1000000), &ms)) {
|
||||
if (ms < 0) ms = 0;
|
||||
if (ms <= 0x7fffffff) timeout = (i32)ms;
|
||||
}
|
||||
Sleep(timeout);
|
||||
if (r) {
|
||||
r->tv_sec = 0;
|
||||
r->tv_nsec = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void thrd_yield()
|
||||
{
|
||||
SwitchToThread();
|
||||
}
|
||||
|
||||
void thrd_exit(int res)
|
||||
{
|
||||
ExitThread(res);
|
||||
}
|
||||
|
||||
int thrd_detach(thrd_t t)
|
||||
{
|
||||
if (!t.__handle) __builtin_trap();
|
||||
return CloseHandle(t.__handle) ? thrd_success : thrd_error;
|
||||
}
|
||||
|
||||
int thrd_join(thrd_t t, int *res)
|
||||
{
|
||||
if (!t.__handle) __builtin_trap();
|
||||
if (WaitForSingleObject(t.__handle, -1)) return thrd_error;
|
||||
if (res) {
|
||||
i32 ec = 0;
|
||||
if (!GetExitCodeThread(t.__handle, &ec)) {
|
||||
CloseHandle(t.__handle);
|
||||
return thrd_error;
|
||||
}
|
||||
*res = ec;
|
||||
}
|
||||
return CloseHandle(t.__handle) ? thrd_success : thrd_error;
|
||||
}
|
||||
|
||||
int mtx_init(mtx_t *m, int type)
|
||||
{
|
||||
if (type & (mtx_recursive | mtx_timed)) return thrd_error;
|
||||
m->__ = 0;
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int mtx_lock(mtx_t *m)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m->__);
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int mtx_timedlock(mtx_t *, const struct timespec *)
|
||||
{
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
int mtx_trylock(mtx_t *m)
|
||||
{
|
||||
return TryAcquireSRWLockExclusive(&m->__) ? thrd_success : thrd_busy;
|
||||
}
|
||||
|
||||
int mtx_unlock(mtx_t *m)
|
||||
{
|
||||
ReleaseSRWLockExclusive(&m->__);
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
void mtx_destroy(mtx_t *)
|
||||
{
|
||||
}
|
||||
|
||||
[[gnu::stdcall]]
|
||||
static i32 once_trampoline(uz, uz parameter, uz *)
|
||||
{
|
||||
void (*func)(void) = (void (*)(void))parameter;
|
||||
func();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void call_once(once_flag *flag, void (*func)(void))
|
||||
{
|
||||
InitOnceExecuteOnce(&flag->__, (uz)&once_trampoline, (uz)func, 0);
|
||||
}
|
||||
|
||||
int cnd_init(cnd_t *c)
|
||||
{
|
||||
c->__ = 0;
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int cnd_signal(cnd_t *c)
|
||||
{
|
||||
WakeConditionVariable(&c->__);
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int cnd_broadcast(cnd_t *c)
|
||||
{
|
||||
WakeAllConditionVariable(&c->__);
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int cnd_wait(cnd_t *c, mtx_t *m)
|
||||
{
|
||||
if (!SleepConditionVariableSRW(&c->__, &m->__, -1, 0)) {
|
||||
return thrd_error;
|
||||
}
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
int cnd_timedwait(cnd_t *c, mtx_t *m, const struct timespec *abs)
|
||||
{
|
||||
// FILETIME is 100ns ticks since 1601-01-01 UTC.
|
||||
// Unix epoch (1970-01-01) is 11644473600 seconds later:
|
||||
// 116444736000000000 ticks.
|
||||
// If the conversion overflows (caller asked for an absurdly-far-future
|
||||
// deadline) or the relative wait exceeds Win32's max finite timeout,
|
||||
// degrade gracefully to an untimed wait.
|
||||
u64 now;
|
||||
GetSystemTimeAsFileTime(&now);
|
||||
i64 abs_ft;
|
||||
i32 timeout = -1;
|
||||
if (!__builtin_mul_overflow((i64)abs->tv_sec, (i64)10000000, &abs_ft) &&
|
||||
!__builtin_add_overflow(abs_ft, (i64)(abs->tv_nsec/100), &abs_ft) &&
|
||||
!__builtin_add_overflow(abs_ft, (i64)116444736000000000, &abs_ft)) {
|
||||
i64 diff = abs_ft - (i64)now;
|
||||
i64 ms = diff <= 0 ? 0 : diff/10000;
|
||||
if (ms <= 0x7fffffff) timeout = (i32)ms;
|
||||
}
|
||||
if (!SleepConditionVariableSRW(&c->__, &m->__, timeout, 0)) {
|
||||
return thrd_timedout;
|
||||
}
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
void cnd_destroy(cnd_t *)
|
||||
{
|
||||
}
|
||||
|
||||
int tss_create(tss_t *key, tss_dtor_t dtor)
|
||||
{
|
||||
i32 k = FlsAlloc((uz)dtor);
|
||||
if (k == -1) return thrd_error;
|
||||
key->__ = k;
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
void *tss_get(tss_t key)
|
||||
{
|
||||
return (void *)FlsGetValue(key.__);
|
||||
}
|
||||
|
||||
int tss_set(tss_t key, void *val)
|
||||
{
|
||||
return FlsSetValue(key.__, (uz)val) ? thrd_success : thrd_error;
|
||||
}
|
||||
|
||||
void tss_delete(tss_t key)
|
||||
{
|
||||
FlsFree(key.__);
|
||||
}
|
||||
76
src/threads.h
Normal file
76
src/threads.h
Normal file
@@ -0,0 +1,76 @@
|
||||
// C11 threads — w64devkit toolchain implementation
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int (*thrd_start_t)(void *);
|
||||
|
||||
enum {
|
||||
thrd_success = 0,
|
||||
thrd_busy = 1,
|
||||
thrd_error = 2,
|
||||
thrd_nomem = 3,
|
||||
thrd_timedout = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
mtx_plain = 0,
|
||||
mtx_recursive = 1,
|
||||
mtx_timed = 2,
|
||||
};
|
||||
|
||||
struct timespec;
|
||||
|
||||
typedef struct {
|
||||
__INT32_TYPE__ __handle;
|
||||
__INT32_TYPE__ __tid;
|
||||
} thrd_t;
|
||||
int thrd_create(thrd_t *, thrd_start_t, void *);
|
||||
int thrd_equal(thrd_t, thrd_t);
|
||||
thrd_t thrd_current(void);
|
||||
int thrd_sleep(const struct timespec *, struct timespec *);
|
||||
void thrd_yield(void);
|
||||
void thrd_exit[[noreturn]](int);
|
||||
int thrd_detach(thrd_t);
|
||||
int thrd_join(thrd_t, int *);
|
||||
|
||||
typedef struct { __UINTPTR_TYPE__ __; } mtx_t;
|
||||
int mtx_init(mtx_t *, int);
|
||||
int mtx_lock(mtx_t *);
|
||||
int mtx_timedlock(mtx_t *, const struct timespec *);
|
||||
int mtx_trylock(mtx_t *);
|
||||
int mtx_unlock(mtx_t *);
|
||||
void mtx_destroy(mtx_t *);
|
||||
|
||||
typedef struct { __UINTPTR_TYPE__ __; } once_flag;
|
||||
#define ONCE_FLAG_INIT {0}
|
||||
void call_once(once_flag *, void (*)(void));
|
||||
|
||||
typedef struct { __UINTPTR_TYPE__ __; } cnd_t;
|
||||
int cnd_init(cnd_t *);
|
||||
int cnd_signal(cnd_t *);
|
||||
int cnd_broadcast(cnd_t *);
|
||||
int cnd_wait(cnd_t *, mtx_t *);
|
||||
int cnd_timedwait(cnd_t *, mtx_t *, const struct timespec *);
|
||||
void cnd_destroy(cnd_t *);
|
||||
|
||||
#if !defined(__cplusplus) && __STDC_VERSION__ < 202311L
|
||||
# define thread_local _Thread_local
|
||||
#endif
|
||||
|
||||
enum { TSS_DTOR_ITERATIONS = 1 };
|
||||
|
||||
typedef struct { __INT32_TYPE__ __; } tss_t;
|
||||
typedef void (*tss_dtor_t)(void *);
|
||||
|
||||
int tss_create(tss_t *, tss_dtor_t);
|
||||
void *tss_get(tss_t);
|
||||
int tss_set(tss_t, void *);
|
||||
void tss_delete(tss_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user