fix: update template, prevent double-spaced persisted kernel logs

This commit is contained in:
2026-08-22 12:47:13 +02:00
parent 903218168d
commit 8a74b771e1
21 changed files with 584 additions and 384 deletions
+47 -9
View File
@@ -44,8 +44,22 @@ namespace montauk {
ThreadEntry user_entry;
void* user_arg;
void* stack_base;
int tid;
ThreadCtx* next;
};
inline ThreadCtx* g_thread_records = nullptr;
inline volatile uint32_t g_thread_records_lock = 0;
inline void records_lock() {
while (__atomic_exchange_n(&g_thread_records_lock, 1, __ATOMIC_ACQUIRE) != 0)
montauk::yield();
}
inline void records_unlock() {
__atomic_store_n(&g_thread_records_lock, 0, __ATOMIC_RELEASE);
}
// Userspace trampoline: bridges from the raw entry the kernel jumps
// to into the typed entry, then funnels into SYS_THREAD_EXIT. We
// route the exit through libc rather than relying on a kernel-side
@@ -53,21 +67,24 @@ namespace montauk {
// memory on this path.
//
// The thread's stack itself is intentionally not freed here: we are
// still running on it. It is reclaimed when the process exits, or
// the joiner may free it explicitly after thread_join.
// still running on it. It is reclaimed by a successful thread_join,
// or as part of whole-process teardown if the thread is never joined.
[[noreturn]] inline void thread_trampoline(detail::ThreadCtx* ctx) {
// A sibling CPU can start the thread before thread_spawn has
// returned its TID. Wait until the parent has published the record
// needed by thread_join to reclaim this stack.
while (__atomic_load_n(&ctx->tid, __ATOMIC_ACQUIRE) == 0)
montauk::yield();
int code = ctx->user_entry(ctx->user_arg);
montauk::mfree(ctx);
thread_exit(code);
}
}
// Spawn a new thread that begins executing `entry(arg)`. Returns the
// new TID on success, or -1 on failure. The thread's stack is
// allocated from the user heap; it is leaked on thread exit (the
// thread itself cannot free the stack it is running on). The kernel
// reclaims it on process exit. Callers that need to spawn many short-
// lived threads should pool stacks themselves.
// allocated from the user heap. The exiting thread cannot free the stack
// it is running on, so thread_join reclaims both it and the trampoline
// context after the kernel has reaped the sibling.
inline int thread_spawn(ThreadEntry entry, void* arg,
uint64_t stack_bytes = 0) {
if (entry == nullptr) return -1;
@@ -84,6 +101,8 @@ namespace montauk {
ctx->user_entry = entry;
ctx->user_arg = arg;
ctx->stack_base = stack;
ctx->tid = 0;
ctx->next = nullptr;
uint64_t stack_top = ((uint64_t)stack + stack_bytes) & ~0xFULL;
int tid = (int)syscall3(montauk::abi::SYS_THREAD_SPAWN,
@@ -94,6 +113,11 @@ namespace montauk {
montauk::mfree(stack);
return -1;
}
detail::records_lock();
ctx->next = detail::g_thread_records;
detail::g_thread_records = ctx;
__atomic_store_n(&ctx->tid, tid, __ATOMIC_RELEASE);
detail::records_unlock();
return tid;
}
@@ -101,8 +125,22 @@ namespace montauk {
// success (with the thread's exit code in *out_code if non-null) or
// -1 if `tid` is not a joinable sibling.
inline int thread_join(int tid, int* out_code = nullptr) {
return (int)syscall2(montauk::abi::SYS_THREAD_JOIN,
(uint64_t)tid, (uint64_t)out_code);
int result = (int)syscall2(montauk::abi::SYS_THREAD_JOIN,
(uint64_t)tid, (uint64_t)out_code);
if (result == 0) {
detail::records_lock();
detail::ThreadCtx** link = &detail::g_thread_records;
while (*link != nullptr && (*link)->tid != tid)
link = &(*link)->next;
detail::ThreadCtx* ctx = *link;
if (ctx != nullptr) *link = ctx->next;
detail::records_unlock();
if (ctx != nullptr) {
montauk::mfree(ctx->stack_base);
montauk::mfree(ctx);
}
}
return result;
}
// Return the calling thread's TID (== getpid() for the main thread).