From 3c8faf03f7fab0d2827f4bd1d23b770956947a1e Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Fri, 17 Jul 2026 22:05:49 +0200 Subject: [PATCH] [Fix] threading_posix: reap thread handle once (mission-end freeze) post_execution() runs on every successful Wait() and a Thread handle stays signaled forever after exit, so multiple guest waits on the same exited handle each call pthread_join() on an already-reaped pthread_t. On glibc that hangs (recycled tid) -> the black-screen freeze at mission teardown. Guard reap with an atomic exchange so it happens exactly once. Isolated from the audio/crash WIP on phase-a-args-fileread; only this hunk, on stock 16e1eb8e2. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/xenia/base/threading_posix.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index b45e72ee0..e74605b28 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -14,6 +14,7 @@ #include "xenia/base/platform.h" #include "xenia/base/threading_timer_queue.h" +#include #include #include #include @@ -981,11 +982,22 @@ class PosixCondition final : public PosixConditionBase { static void* ThreadStartRoutine(void* parameter); bool signaled() const override { return signaled_; } void post_execution() override { + // Reap exactly once. post_execution() runs on EVERY successful Wait(), and + // a thread object stays signaled forever once it has exited -- so every + // later wait on the same handle would pthread_join() a pthread_t that the + // first join already reaped and freed. On glibc that faults or, if the tid + // was recycled, blocks forever inside pthread_join(). The guest does + // exactly this at mission teardown (several waiters on one thread handle), + // which hung the waiting guest thread: the game froze to a black screen. + if (reaped_.exchange(true)) { + return; + } if (thread_) { pthread_join(thread_, nullptr); } sem_destroy(&suspend_sem_); } + std::atomic reaped_{false}; pthread_t thread_; pid_t tid_ = 0; // Kernel TID for setpriority() fallback mutable bool fifo_failed_ = false; // True after SCHED_FIFO was rejected