mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 09:51:02 +00:00
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.
89 lines
2.6 KiB
C
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;
|
|
}
|