mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-18 17:06:12 +08:00
Two follow-ups to upstream #102's condition-variable changes, as analyzed in upstream issue #113: - The pending-signal consume path reacquired the guest mutex while still holding the condition state lock, inverting lock order against cond-signal (mutex -> SyncRoot) and deadlocking both threads. Leave the condition lock before relocking, matching the normal wake path. This unfroze Dreaming Sarah (PPSA02929) at its title screen. - pthread_cond_timedwait's third argument is a pointer to an absolute CLOCK_REALTIME timespec, not a relative microsecond count; the guest address was being truncated into a duration, yielding arbitrary timeouts. Read the timespec and convert to a relative wait. scePthreadCondTimedwait keeps its separate relative-time ABI.
This commit is contained in:
@@ -349,7 +349,32 @@ public static class KernelPthreadCompatExports
|
|||||||
ExportName = "pthread_cond_timedwait",
|
ExportName = "pthread_cond_timedwait",
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
LibraryName = "libKernel")]
|
LibraryName = "libKernel")]
|
||||||
public static int PosixPthreadCondTimedwait(CpuContext ctx) => PthreadCondTimedwait(ctx);
|
public static int PosixPthreadCondTimedwait(CpuContext ctx)
|
||||||
|
{
|
||||||
|
// POSIX passes `const struct timespec *abstime` (absolute deadline), not relative microseconds (#113).
|
||||||
|
var abstimeAddress = ctx[CpuRegister.Rdx];
|
||||||
|
if (abstimeAddress == 0)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryReadUInt64(abstimeAddress, out var deadlineSeconds) ||
|
||||||
|
!ctx.TryReadUInt64(abstimeAddress + 8, out var deadlineNanoseconds))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nowMicroseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1000L;
|
||||||
|
var deadlineMicroseconds =
|
||||||
|
deadlineSeconds >= long.MaxValue / 1_000_000UL
|
||||||
|
? long.MaxValue
|
||||||
|
: (long)(deadlineSeconds * 1_000_000UL + Math.Min(deadlineNanoseconds, 999_999_999UL) / 1000UL);
|
||||||
|
var remainingMicroseconds = deadlineMicroseconds - nowMicroseconds;
|
||||||
|
var timeoutUsec = remainingMicroseconds <= 0
|
||||||
|
? 1u
|
||||||
|
: (uint)Math.Min(remainingMicroseconds, uint.MaxValue);
|
||||||
|
return PthreadCondWaitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi], timed: true, timeoutUsec: timeoutUsec);
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "kDh-NfxgMtE",
|
Nid = "kDh-NfxgMtE",
|
||||||
@@ -1185,10 +1210,19 @@ public static class KernelPthreadCompatExports
|
|||||||
|
|
||||||
if (consumedPendingSignal)
|
if (consumedPendingSignal)
|
||||||
{
|
{
|
||||||
|
// Leave SyncRoot before relocking the mutex to avoid lock-order inversion with cond-signal (#113).
|
||||||
state.Waiters = Math.Max(0, state.Waiters - 1);
|
state.Waiters = Math.Max(0, state.Waiters - 1);
|
||||||
TracePthreadCond("wait-wake-pending", condAddress, mutexAddress, state, timed, waitResult);
|
TracePthreadCond("wait-wake-pending", condAddress, mutexAddress, state, timed, waitResult);
|
||||||
var pendingLockResult = PthreadMutexRelockForCondWait(ctx, mutexAddress, releasedRecursion);
|
Monitor.Exit(state.SyncRoot);
|
||||||
return pendingLockResult != (int)OrbisGen2Result.ORBIS_GEN2_OK ? pendingLockResult : waitResult;
|
try
|
||||||
|
{
|
||||||
|
var pendingLockResult = PthreadMutexRelockForCondWait(ctx, mutexAddress, releasedRecursion);
|
||||||
|
return pendingLockResult != (int)OrbisGen2Result.ORBIS_GEN2_OK ? pendingLockResult : waitResult;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Monitor.Enter(state.SyncRoot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var scheduler = GuestThreadExecution.Scheduler;
|
var scheduler = GuestThreadExecution.Scheduler;
|
||||||
|
|||||||
Reference in New Issue
Block a user