[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) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-17 22:05:49 +02:00
parent 16e1eb8e28
commit 3c8faf03f7

View File

@@ -14,6 +14,7 @@
#include "xenia/base/platform.h"
#include "xenia/base/threading_timer_queue.h"
#include <atomic>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
@@ -981,11 +982,22 @@ class PosixCondition<Thread> 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<bool> 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