Files
totk-pc-recomp/runtime/platform/common.c
T
Wellington Dias 1d51343ac1 Build and run the runtime natively on Linux.
Shared runtime code now goes through runtime/platform/platform.h, implemented by platform/win32.c and a new
platform/linux.c: futex locks, a userfaultfd and PAGEMAP_SCAN write watch, signal-based async preemption, our own
fiber switch, and a SIGSEGV handler for the thaw and crash hooks. The window, input, menu and software keyboard get
SDL3 back ends beside the Win32 ones, with the swapchain (gfx_present.c) and input merging (input.c) shared. SHA-256
replaces BCrypt, ELF builds get their own override sections, and the build tools gain Linux branches.

On Linux the selftest and the gfx-abi, astc, nvn and window probes pass. probe-vkmem still fails: imported guest
memory cannot be write-watched on Linux, which the shadow buffers in vk_memory.c will address.
2026-10-29 16:56:56 -03:00

89 lines
2.6 KiB
C

// The parts of platform.h built on its locks rather than on the OS: recursive mutexes, one-time initialisation and
// events. Every OS shares them.
#include "platform.h"
// ---- recursive mutex: owner is the holder's thread id (0 when free); only the holder writes depth.
void plat_rmutex_lock(PlatRMutex *m) {
uint32_t me = plat_thread_id();
if (__atomic_load_n(&m->owner, __ATOMIC_RELAXED) == me) {
m->depth++;
return;
}
plat_mutex_lock(&m->m);
__atomic_store_n(&m->owner, me, __ATOMIC_RELAXED);
m->depth = 1;
}
void plat_rmutex_unlock(PlatRMutex *m) {
if (--m->depth) return;
__atomic_store_n(&m->owner, 0, __ATOMIC_RELAXED);
plat_mutex_unlock(&m->m);
}
// ---- once: state 0 not run, 1 running, 2 done. Waiters for a running initialiser sleep on one process-wide
// condition variable; initialisers are rare and short.
static PlatMutex once_lock = PLAT_MUTEX_INIT;
static PlatCond once_done = PLAT_COND_INIT;
void plat_once(PlatOnce *o, void (*fn)(void *arg), void *arg) {
if (__atomic_load_n(&o->state, __ATOMIC_ACQUIRE) == 2) return;
int32_t expected = 0;
if (__atomic_compare_exchange_n(&o->state, &expected, 1, 0, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
fn(arg);
plat_mutex_lock(&once_lock);
__atomic_store_n(&o->state, 2, __ATOMIC_RELEASE);
plat_cond_broadcast(&once_done);
plat_mutex_unlock(&once_lock);
return;
}
plat_mutex_lock(&once_lock);
while (__atomic_load_n(&o->state, __ATOMIC_ACQUIRE) != 2) plat_cond_wait(&once_done, &once_lock);
plat_mutex_unlock(&once_lock);
}
// ---- events
void plat_event_init(PlatEvent *e, int manual, int initially_set) {
plat_mutex_init(&e->m);
plat_cond_init(&e->c);
e->manual = manual;
e->set = initially_set;
}
void plat_event_set(PlatEvent *e) {
plat_mutex_lock(&e->m);
e->set = 1;
if (e->manual) plat_cond_broadcast(&e->c);
else plat_cond_signal(&e->c);
plat_mutex_unlock(&e->m);
}
void plat_event_reset(PlatEvent *e) {
plat_mutex_lock(&e->m);
e->set = 0;
plat_mutex_unlock(&e->m);
}
void plat_event_wait(PlatEvent *e) {
plat_mutex_lock(&e->m);
while (!e->set) plat_cond_wait(&e->c, &e->m);
if (!e->manual) e->set = 0;
plat_mutex_unlock(&e->m);
}
int plat_event_wait_ns(PlatEvent *e, uint64_t ns) {
uint64_t end = plat_now_ns() + ns;
plat_mutex_lock(&e->m);
while (!e->set) {
uint64_t now = plat_now_ns();
if (now >= end) break;
plat_cond_wait_ns(&e->c, &e->m, end - now);
}
int got = e->set;
if (got && !e->manual) e->set = 0;
plat_mutex_unlock(&e->m);
return got;
}