mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 10:56:20 +08:00
[Kernel] Hand off mutex ownership directly to the head waiter on unlock (#439)
pthread_mutex_unlock cleared ownership (OwnerThreadId = 0) and only woke the head waiter, relying on that woken thread to re-acquire the lock itself. If the wake raced or was lost, the mutex was left "free but with a queued waiter" — a state the fast-acquire path in PthreadMutexLockCore explicitly refuses (OwnerThreadId == 0 && Waiters.Count == 0), so every later locker, including the game's main thread, queued behind a head that never advanced and the whole process wedged. Grant the mutex to the head waiter directly inside unlock (the same TryGrantMutexWaiterLocked hand-off the thread-exit cleanup already uses), then wake it. The mutex is therefore never observable as free-with-waiter. Verified against The Invincible (PPSA06426): forward progress jumps from ~3.5M to ~40M dispatched imports and the repeated unlock INVALID_ARGUMENT errors disappear. No regression on Dead Cells (PPSA15552), which still reaches AGC rendering with zero mutex errors.
This commit is contained in:
@@ -777,9 +777,21 @@ public static class KernelPthreadCompatExports
|
||||
if (state.RecursionCount == 0)
|
||||
{
|
||||
state.OwnerThreadId = 0;
|
||||
nextWakeKey = state.Waiters.First?.Value.Cooperative == true
|
||||
? state.Waiters.First.Value.WakeKey
|
||||
: null;
|
||||
|
||||
// Hand the mutex directly to the head waiter instead of only
|
||||
// waking it and relying on it to re-acquire. A woken waiter that
|
||||
// fails to self-grant (its wake races or is lost) would leave the
|
||||
// mutex "free with a queued waiter"; the fast-acquire path refuses
|
||||
// such a mutex (OwnerThreadId == 0 && Waiters.Count == 0), so every
|
||||
// later locker — including the game's main thread — then queues
|
||||
// behind a head that never advances and the process wedges.
|
||||
if (state.Waiters.First is { } headNode &&
|
||||
TryGrantMutexWaiterLocked(state, headNode.Value) &&
|
||||
headNode.Value.Cooperative)
|
||||
{
|
||||
nextWakeKey = headNode.Value.WakeKey;
|
||||
}
|
||||
|
||||
Monitor.PulseAll(state);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user