Files
sharpemu/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs
T
2026-04-13 15:54:38 +03:00

1067 lines
37 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace SharpEmu.Libs.Kernel;
public static class KernelPthreadCompatExports
{
private const int MutexTypeDefault = 1;
private const int MutexTypeErrorCheck = 1;
private const int MutexTypeRecursive = 2;
private const int MutexTypeNormal = 3;
private const int MutexTypeAdaptiveNp = 4;
private const ulong StaticAdaptiveMutexInitializer = 1;
private const ulong SyntheticMutexHandleBase = 0x00006000_0000_0000;
private const ulong SyntheticMutexAttrHandleBase = 0x00006001_0000_0000;
private const ulong SyntheticCondHandleBase = 0x00006002_0000_0000;
private static readonly object _stateGate = new();
private static readonly Dictionary<ulong, PthreadMutexState> _mutexStates = new();
private static readonly Dictionary<ulong, PthreadMutexAttrState> _mutexAttrStates = new();
private static readonly Dictionary<ulong, PthreadCondState> _condStates = new();
private static readonly HashSet<ulong> _condAttrStates = new();
private static long _nextSyntheticMutexHandleId = 1;
private static long _nextSyntheticMutexAttrHandleId = 1;
private static long _nextSyntheticCondHandleId = 1;
private sealed class PthreadMutexState
{
public SemaphoreSlim Semaphore { get; } = new(1, 1);
public ulong OwnerThreadId { get; set; }
public int RecursionCount { get; set; }
public int Type { get; set; } = MutexTypeDefault;
public int Protocol { get; set; }
}
private sealed class PthreadCondState
{
public object SyncRoot { get; } = new();
public ulong SignalEpoch { get; set; }
public int Waiters { get; set; }
}
private readonly record struct PthreadMutexAttrState(int Type, int Protocol);
[SysAbiExport(
Nid = "aI+OeCz8xrQ",
ExportName = "scePthreadSelf",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadSelf(CpuContext ctx)
{
var currentThreadHandle = KernelPthreadState.GetCurrentThreadHandle();
ctx[CpuRegister.Rax] = currentThreadHandle;
TracePthreadSelf(ctx, currentThreadHandle);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "3PtV6p3QNX4",
ExportName = "scePthreadEqual",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadEqual(CpuContext ctx)
{
var left = ctx[CpuRegister.Rdi];
var right = ctx[CpuRegister.Rsi];
ctx[CpuRegister.Rax] = left == right ? 1UL : 0UL;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "T72hz6ffq08",
ExportName = "scePthreadYield",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadYield(CpuContext ctx)
{
_ = ctx;
Thread.Yield();
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "EI-5-jlq2dE",
ExportName = "scePthreadGetthreadid",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadGetthreadid(CpuContext ctx) => PthreadGetthreadidCore(ctx);
[SysAbiExport(
Nid = "3eqs37G74-s",
ExportName = "pthread_getthreadid_np",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadGetthreadidNp(CpuContext ctx) => PthreadGetthreadidCore(ctx);
[SysAbiExport(
Nid = "cmo1RIYva9o",
ExportName = "scePthreadMutexInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexInit(CpuContext ctx) => PthreadMutexInitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi]);
[SysAbiExport(
Nid = "2Of0f+3mhhE",
ExportName = "scePthreadMutexDestroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexDestroy(CpuContext ctx) => PthreadMutexDestroyCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "9UK1vLZQft4",
ExportName = "scePthreadMutexLock",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexLock(CpuContext ctx) => PthreadMutexLockCore(ctx, ctx[CpuRegister.Rdi], tryOnly: false);
[SysAbiExport(
Nid = "upoVrzMHFeE",
ExportName = "scePthreadMutexTrylock",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexTrylock(CpuContext ctx) => PthreadMutexLockCore(ctx, ctx[CpuRegister.Rdi], tryOnly: true);
[SysAbiExport(
Nid = "tn3VlD0hG60",
ExportName = "scePthreadMutexUnlock",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexUnlock(CpuContext ctx) => PthreadMutexUnlockCore(ctx, ctx[CpuRegister.Rdi], requireOwner: true);
[SysAbiExport(
Nid = "ttHNfU+qDBU",
ExportName = "pthread_mutex_init",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexInit(CpuContext ctx) => PthreadMutexInitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi]);
[SysAbiExport(
Nid = "ltCfaGr2JGE",
ExportName = "pthread_mutex_destroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexDestroy(CpuContext ctx) => PthreadMutexDestroyCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "7H0iTOciTLo",
ExportName = "pthread_mutex_lock",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexLock(CpuContext ctx) => PthreadMutexLockCore(ctx, ctx[CpuRegister.Rdi], tryOnly: false);
[SysAbiExport(
Nid = "K-jXhbt2gn4",
ExportName = "pthread_mutex_trylock",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexTrylock(CpuContext ctx) => PthreadMutexLockCore(ctx, ctx[CpuRegister.Rdi], tryOnly: true);
[SysAbiExport(
Nid = "2Z+PpY6CaJg",
ExportName = "pthread_mutex_unlock",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexUnlock(CpuContext ctx) => PthreadMutexUnlockCore(ctx, ctx[CpuRegister.Rdi], requireOwner: true);
private static int PthreadGetthreadidCore(CpuContext ctx)
{
ctx[CpuRegister.Rax] = KernelPthreadState.GetCurrentThreadUniqueId();
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "F8bUHwAG284",
ExportName = "scePthreadMutexattrInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexattrInit(CpuContext ctx) => PthreadMutexattrInitCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "smWEktiyyG0",
ExportName = "scePthreadMutexattrDestroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexattrDestroy(CpuContext ctx) => PthreadMutexattrDestroyCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "iMp8QpE+XO4",
ExportName = "scePthreadMutexattrSettype",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexattrSettype(CpuContext ctx) => PthreadMutexattrSettypeCore(ctx, ctx[CpuRegister.Rdi], unchecked((int)ctx[CpuRegister.Rsi]));
[SysAbiExport(
Nid = "1FGvU0i9saQ",
ExportName = "scePthreadMutexattrSetprotocol",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadMutexattrSetprotocol(CpuContext ctx) => PthreadMutexattrSetprotocolCore(ctx, ctx[CpuRegister.Rdi], unchecked((int)ctx[CpuRegister.Rsi]));
[SysAbiExport(
Nid = "dQHWEsJtoE4",
ExportName = "pthread_mutexattr_init",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexattrInit(CpuContext ctx) => PthreadMutexattrInitCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "HF7lK46xzjY",
ExportName = "pthread_mutexattr_destroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexattrDestroy(CpuContext ctx) => PthreadMutexattrDestroyCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "mDmgMOGVUqg",
ExportName = "pthread_mutexattr_settype",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexattrSettype(CpuContext ctx) => PthreadMutexattrSettypeCore(ctx, ctx[CpuRegister.Rdi], unchecked((int)ctx[CpuRegister.Rsi]));
[SysAbiExport(
Nid = "5txKfcMUAok",
ExportName = "pthread_mutexattr_setprotocol",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadMutexattrSetprotocol(CpuContext ctx) => PthreadMutexattrSetprotocolCore(ctx, ctx[CpuRegister.Rdi], unchecked((int)ctx[CpuRegister.Rsi]));
[SysAbiExport(
Nid = "2Tb92quprl0",
ExportName = "scePthreadCondInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondInit(CpuContext ctx) => PthreadCondInitCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "0TyVk4MSLt0",
ExportName = "pthread_cond_init",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadCondInit(CpuContext ctx) => PthreadCondInitCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "g+PZd2hiacg",
ExportName = "scePthreadCondDestroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondDestroy(CpuContext ctx) => PthreadCondDestroyCore(ctx, ctx[CpuRegister.Rdi]);
[SysAbiExport(
Nid = "WKAXJ4XBPQ4",
ExportName = "scePthreadCondWait",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondWait(CpuContext ctx) => PthreadCondWaitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi], timed: false);
[SysAbiExport(
Nid = "BmMjYxmew1w",
ExportName = "scePthreadCondTimedwait",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondTimedwait(CpuContext ctx) => PthreadCondWaitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi], timed: true, timeoutUsec: unchecked((uint)ctx[CpuRegister.Rdx]));
[SysAbiExport(
Nid = "kDh-NfxgMtE",
ExportName = "scePthreadCondSignal",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondSignal(CpuContext ctx) => PthreadCondSignalCore(ctx, ctx[CpuRegister.Rdi], broadcast: false);
[SysAbiExport(
Nid = "JGgj7Uvrl+A",
ExportName = "scePthreadCondBroadcast",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondBroadcast(CpuContext ctx) => PthreadCondSignalCore(ctx, ctx[CpuRegister.Rdi], broadcast: true);
[SysAbiExport(
Nid = "Op8TBGY5KHg",
ExportName = "pthread_cond_wait",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadCondWait(CpuContext ctx) => PthreadCondWaitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi], timed: false);
[SysAbiExport(
Nid = "mkx2fVhNMsg",
ExportName = "pthread_cond_broadcast",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadCondBroadcast(CpuContext ctx) => PthreadCondSignalCore(ctx, ctx[CpuRegister.Rdi], broadcast: true);
[SysAbiExport(
Nid = "2MOy+rUfuhQ",
ExportName = "pthread_cond_signal",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PosixPthreadCondSignal(CpuContext ctx) => PthreadCondSignalCore(ctx, ctx[CpuRegister.Rdi], broadcast: false);
[SysAbiExport(
Nid = "m5-2bsNfv7s",
ExportName = "scePthreadCondattrInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondattrInit(CpuContext ctx)
{
var attrAddress = ctx[CpuRegister.Rdi];
if (attrAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
lock (_stateGate)
{
_condAttrStates.Add(attrAddress);
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "waPcxYiR3WA",
ExportName = "scePthreadCondattrDestroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadCondattrDestroy(CpuContext ctx)
{
var attrAddress = ctx[CpuRegister.Rdi];
if (attrAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
lock (_stateGate)
{
_condAttrStates.Remove(attrAddress);
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexInitCore(CpuContext ctx, ulong mutexAddress, ulong attrAddress)
{
if (mutexAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var attr = ResolveMutexAttrState(ctx, attrAddress);
var state = new PthreadMutexState
{
Type = attr.Type,
Protocol = attr.Protocol,
};
var syntheticHandle = AllocateSyntheticHandle(SyntheticMutexHandleBase, ref _nextSyntheticMutexHandleId);
lock (_stateGate)
{
_mutexStates[mutexAddress] = state;
_mutexStates[syntheticHandle] = state;
}
_ = ctx.TryWriteUInt64(mutexAddress, syntheticHandle);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexDestroyCore(CpuContext ctx, ulong mutexAddress)
{
if (mutexAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var resolvedAddress = ResolveMutexHandle(ctx, mutexAddress);
PthreadMutexState? state;
lock (_stateGate)
{
_mutexStates.TryGetValue(resolvedAddress, out state);
_mutexStates.Remove(resolvedAddress);
if (resolvedAddress != mutexAddress)
{
_mutexStates.Remove(mutexAddress);
}
}
if (state is null)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
_ = ctx.TryWriteUInt64(mutexAddress, 0);
state.Semaphore.Dispose();
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexLockCore(CpuContext ctx, ulong mutexAddress, bool tryOnly)
{
if (mutexAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
if (!TryResolveMutexState(ctx, mutexAddress, createIfZero: true, out var resolvedAddress, out var state))
{
TracePthreadMutex(ctx, tryOnly ? "trylock" : "lock", mutexAddress, resolvedAddress, null, KernelPthreadState.GetCurrentThreadHandle(), (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
var currentThreadId = KernelPthreadState.GetCurrentThreadHandle();
lock (state)
{
if (state.OwnerThreadId == currentThreadId)
{
if (state.Type == MutexTypeRecursive)
{
state.RecursionCount++;
TracePthreadMutex(ctx, tryOnly ? "trylock" : "lock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_OK);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
if (state.Type is MutexTypeNormal or MutexTypeAdaptiveNp)
{
if (tryOnly)
{
TracePthreadMutex(ctx, "trylock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY;
}
// Normal/adaptive mutexes do not report EDEADLK on self-lock.
// Under the current single-host-thread guest execution model,
// treating them as nested ownership keeps init paths moving
// without turning a would-block path into a hard error.
state.RecursionCount++;
TracePthreadMutex(ctx, "lock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_OK);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
var ownedResult = tryOnly
? (int)OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_DEADLOCK;
TracePthreadMutex(ctx, tryOnly ? "trylock" : "lock", mutexAddress, resolvedAddress, state, currentThreadId, ownedResult);
return ownedResult;
}
}
var acquired = true;
if (tryOnly)
{
acquired = state.Semaphore.Wait(0);
}
else
{
state.Semaphore.Wait();
}
if (!acquired)
{
TracePthreadMutex(ctx, tryOnly ? "trylock" : "lock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY;
}
lock (state)
{
state.OwnerThreadId = currentThreadId;
state.RecursionCount = 1;
}
TracePthreadMutex(ctx, tryOnly ? "trylock" : "lock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_OK);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexUnlockCore(CpuContext ctx, ulong mutexAddress, bool requireOwner)
{
if (mutexAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
if (!TryResolveMutexState(ctx, mutexAddress, createIfZero: true, out var resolvedAddress, out var state))
{
TracePthreadMutex(ctx, "unlock", mutexAddress, resolvedAddress, null, KernelPthreadState.GetCurrentThreadHandle(), (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
var currentThreadId = KernelPthreadState.GetCurrentThreadHandle();
var shouldRelease = false;
lock (state)
{
if (state.RecursionCount <= 0)
{
TracePthreadMutex(ctx, "unlock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
if (requireOwner && state.OwnerThreadId != currentThreadId)
{
TracePthreadMutex(ctx, "unlock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_PERMISSION_DENIED);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_PERMISSION_DENIED;
}
state.RecursionCount--;
if (state.RecursionCount == 0)
{
state.OwnerThreadId = 0;
shouldRelease = true;
}
}
if (shouldRelease)
{
try
{
state.Semaphore.Release();
}
catch (SemaphoreFullException)
{
TracePthreadMutex(ctx, "unlock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
}
TracePthreadMutex(ctx, "unlock", mutexAddress, resolvedAddress, state, currentThreadId, (int)OrbisGen2Result.ORBIS_GEN2_OK);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexattrInitCore(CpuContext ctx, ulong attrAddress)
{
if (attrAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var syntheticHandle = AllocateSyntheticHandle(SyntheticMutexAttrHandleBase, ref _nextSyntheticMutexAttrHandleId);
lock (_stateGate)
{
_mutexAttrStates[attrAddress] = new PthreadMutexAttrState(MutexTypeDefault, 0);
_mutexAttrStates[syntheticHandle] = new PthreadMutexAttrState(MutexTypeDefault, 0);
}
_ = ctx.TryWriteUInt64(attrAddress, syntheticHandle);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexattrDestroyCore(CpuContext ctx, ulong attrAddress)
{
if (attrAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var resolvedAddress = ResolveMutexAttrHandle(ctx, attrAddress);
lock (_stateGate)
{
_mutexAttrStates.Remove(resolvedAddress);
if (resolvedAddress != attrAddress)
{
_mutexAttrStates.Remove(attrAddress);
}
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexattrSettypeCore(CpuContext ctx, ulong attrAddress, int type)
{
if (attrAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var resolvedAddress = ResolveMutexAttrHandle(ctx, attrAddress);
lock (_stateGate)
{
if (!_mutexAttrStates.TryGetValue(resolvedAddress, out var state))
{
state = new PthreadMutexAttrState(MutexTypeDefault, 0);
}
_mutexAttrStates[resolvedAddress] = state with { Type = NormalizeMutexType(type) };
if (resolvedAddress != attrAddress)
{
_mutexAttrStates[attrAddress] = _mutexAttrStates[resolvedAddress];
}
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadMutexattrSetprotocolCore(CpuContext ctx, ulong attrAddress, int protocol)
{
if (attrAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var resolvedAddress = ResolveMutexAttrHandle(ctx, attrAddress);
lock (_stateGate)
{
if (!_mutexAttrStates.TryGetValue(resolvedAddress, out var state))
{
state = new PthreadMutexAttrState(MutexTypeDefault, 0);
}
_mutexAttrStates[resolvedAddress] = state with { Protocol = protocol };
if (resolvedAddress != attrAddress)
{
_mutexAttrStates[attrAddress] = _mutexAttrStates[resolvedAddress];
}
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static ulong ResolveMutexHandle(CpuContext ctx, ulong mutexAddress)
{
if (mutexAddress == 0)
{
return 0;
}
lock (_stateGate)
{
if (_mutexStates.ContainsKey(mutexAddress))
{
return mutexAddress;
}
}
if (ctx.TryReadUInt64(mutexAddress, out var pointedHandle) && pointedHandle != 0)
{
lock (_stateGate)
{
if (_mutexStates.ContainsKey(pointedHandle))
{
return pointedHandle;
}
}
}
return mutexAddress;
}
private static bool TryResolveMutexState(CpuContext ctx, ulong mutexAddress, bool createIfZero, out ulong resolvedAddress, [NotNullWhen(true)] out PthreadMutexState? state)
{
resolvedAddress = 0;
state = null;
if (mutexAddress == 0)
{
return false;
}
lock (_stateGate)
{
if (_mutexStates.TryGetValue(mutexAddress, out state))
{
resolvedAddress = mutexAddress;
return true;
}
}
if (!ctx.TryReadUInt64(mutexAddress, out var pointedHandle))
{
return false;
}
if (pointedHandle == StaticAdaptiveMutexInitializer)
{
return CreateImplicitMutexState(ctx, mutexAddress, MutexTypeAdaptiveNp, out resolvedAddress, out state);
}
if (pointedHandle != 0)
{
lock (_stateGate)
{
if (_mutexStates.TryGetValue(pointedHandle, out state))
{
_mutexStates[mutexAddress] = state;
resolvedAddress = pointedHandle;
return true;
}
}
resolvedAddress = pointedHandle;
return false;
}
if (!createIfZero)
{
resolvedAddress = mutexAddress;
return false;
}
return CreateImplicitMutexState(ctx, mutexAddress, MutexTypeDefault, out resolvedAddress, out state);
}
private static ulong ResolveMutexAttrHandle(CpuContext ctx, ulong attrAddress)
{
if (attrAddress == 0)
{
return 0;
}
lock (_stateGate)
{
if (_mutexAttrStates.ContainsKey(attrAddress))
{
return attrAddress;
}
}
if (ctx.TryReadUInt64(attrAddress, out var pointedHandle) && pointedHandle != 0)
{
lock (_stateGate)
{
if (_mutexAttrStates.ContainsKey(pointedHandle))
{
return pointedHandle;
}
}
}
return attrAddress;
}
private static PthreadMutexAttrState ResolveMutexAttrState(CpuContext ctx, ulong attrAddress)
{
if (attrAddress == 0)
{
return default;
}
var resolvedAddress = ResolveMutexAttrHandle(ctx, attrAddress);
lock (_stateGate)
{
return _mutexAttrStates.TryGetValue(resolvedAddress, out var state)
? state
: new PthreadMutexAttrState(MutexTypeDefault, 0);
}
}
private static ulong ResolveCondHandle(CpuContext ctx, ulong condAddress)
{
if (condAddress == 0)
{
return 0;
}
lock (_stateGate)
{
if (_condStates.ContainsKey(condAddress))
{
return condAddress;
}
}
if (ctx.TryReadUInt64(condAddress, out var pointedHandle) && pointedHandle != 0)
{
lock (_stateGate)
{
if (_condStates.ContainsKey(pointedHandle))
{
return pointedHandle;
}
}
}
return condAddress;
}
private static bool TryResolveCondState(CpuContext? ctx, ulong condAddress, bool createIfZero, out ulong resolvedAddress, [NotNullWhen(true)] out PthreadCondState? state)
{
resolvedAddress = 0;
state = null;
if (condAddress == 0)
{
return false;
}
lock (_stateGate)
{
if (_condStates.TryGetValue(condAddress, out state))
{
resolvedAddress = condAddress;
return true;
}
}
if (ctx is null || !ctx.TryReadUInt64(condAddress, out var pointedHandle))
{
return false;
}
if (pointedHandle != 0)
{
lock (_stateGate)
{
if (_condStates.TryGetValue(pointedHandle, out state))
{
_condStates[condAddress] = state;
resolvedAddress = pointedHandle;
return true;
}
}
resolvedAddress = pointedHandle;
return false;
}
if (!createIfZero)
{
resolvedAddress = condAddress;
return false;
}
var createdState = new PthreadCondState();
var syntheticHandle = AllocateSyntheticHandle(SyntheticCondHandleBase, ref _nextSyntheticCondHandleId);
lock (_stateGate)
{
_condStates[condAddress] = createdState;
_condStates[syntheticHandle] = createdState;
}
_ = ctx.TryWriteUInt64(condAddress, syntheticHandle);
resolvedAddress = syntheticHandle;
state = createdState;
return true;
}
private static ulong AllocateSyntheticHandle(ulong baseAddress, ref long nextId)
{
var id = unchecked((ulong)Interlocked.Increment(ref nextId));
return baseAddress + (id << 4);
}
private static int PthreadCondInitCore(CpuContext ctx, ulong condAddress)
{
if (condAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var syntheticHandle = AllocateSyntheticHandle(SyntheticCondHandleBase, ref _nextSyntheticCondHandleId);
lock (_stateGate)
{
var state = new PthreadCondState();
_condStates[condAddress] = state;
_condStates[syntheticHandle] = state;
}
_ = ctx.TryWriteUInt64(condAddress, syntheticHandle);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadCondDestroyCore(CpuContext ctx, ulong condAddress)
{
if (condAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var resolvedAddress = ResolveCondHandle(ctx, condAddress);
lock (_stateGate)
{
_condStates.Remove(resolvedAddress);
if (resolvedAddress != condAddress)
{
_condStates.Remove(condAddress);
}
}
_ = ctx.TryWriteUInt64(condAddress, 0);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PthreadCondWaitCore(CpuContext ctx, ulong condAddress, ulong mutexAddress, bool timed, uint timeoutUsec = 0)
{
if (condAddress == 0 || mutexAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
if (!TryResolveCondState(ctx, condAddress, createIfZero: true, out _, out var state))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
var waitResult = (int)OrbisGen2Result.ORBIS_GEN2_OK;
lock (state.SyncRoot)
{
state.Waiters++;
var observedEpoch = state.SignalEpoch;
TracePthreadCond("wait-enter", condAddress, mutexAddress, state, timed, waitResult);
var unlockResult = PthreadMutexUnlockCore(ctx, mutexAddress, requireOwner: true);
if (unlockResult != (int)OrbisGen2Result.ORBIS_GEN2_OK)
{
state.Waiters--;
TracePthreadCond("wait-unlock-fail", condAddress, mutexAddress, state, timed, unlockResult);
return unlockResult;
}
while (state.SignalEpoch == observedEpoch)
{
if (!timed)
{
Monitor.Wait(state.SyncRoot);
continue;
}
if (!Monitor.Wait(state.SyncRoot, GetCondWaitTimeout(timeoutUsec)))
{
waitResult = (int)OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT;
break;
}
}
state.Waiters = Math.Max(0, state.Waiters - 1);
TracePthreadCond(waitResult == (int)OrbisGen2Result.ORBIS_GEN2_OK ? "wait-wake" : "wait-timeout", condAddress, mutexAddress, state, timed, waitResult);
}
var lockResult = PthreadMutexLockCore(ctx, mutexAddress, tryOnly: false);
if (lockResult != (int)OrbisGen2Result.ORBIS_GEN2_OK)
{
TracePthreadCond("wait-relock-fail", condAddress, mutexAddress, state, timed, lockResult);
return lockResult;
}
TracePthreadCond(waitResult == (int)OrbisGen2Result.ORBIS_GEN2_OK ? "wait-exit" : "wait-exit-timeout", condAddress, mutexAddress, state, timed, waitResult);
return waitResult;
}
private static int PthreadCondSignalCore(CpuContext ctx, ulong condAddress, bool broadcast)
{
if (condAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
if (!TryResolveCondState(ctx, condAddress, createIfZero: true, out _, out var state))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
lock (state.SyncRoot)
{
if (state.Waiters > 0)
{
state.SignalEpoch++;
if (broadcast)
{
Monitor.PulseAll(state.SyncRoot);
}
else
{
Monitor.Pulse(state.SyncRoot);
}
}
TracePthreadCond(broadcast ? "broadcast" : "signal", condAddress, mutexAddress: 0, state, timed: false, (int)OrbisGen2Result.ORBIS_GEN2_OK);
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static TimeSpan GetCondWaitTimeout(uint timeoutUsec)
{
if (timeoutUsec == 0)
{
return TimeSpan.Zero;
}
return TimeSpan.FromTicks((long)timeoutUsec * 10L);
}
private static int NormalizeMutexType(int type)
{
return type switch
{
0 => MutexTypeDefault,
1 => MutexTypeErrorCheck,
2 => MutexTypeRecursive,
3 => MutexTypeNormal,
4 => MutexTypeAdaptiveNp,
_ => MutexTypeDefault,
};
}
private static bool CreateImplicitMutexState(CpuContext ctx, ulong mutexAddress, int type, out ulong resolvedAddress, [NotNullWhen(true)] out PthreadMutexState? state)
{
var createdState = new PthreadMutexState
{
Type = type,
};
var syntheticHandle = AllocateSyntheticHandle(SyntheticMutexHandleBase, ref _nextSyntheticMutexHandleId);
lock (_stateGate)
{
if (_mutexStates.TryGetValue(mutexAddress, out state))
{
resolvedAddress = mutexAddress;
return true;
}
if (_mutexStates.TryGetValue(syntheticHandle, out state))
{
resolvedAddress = syntheticHandle;
return true;
}
_mutexStates[mutexAddress] = createdState;
_mutexStates[syntheticHandle] = createdState;
}
_ = ctx.TryWriteUInt64(mutexAddress, syntheticHandle);
resolvedAddress = syntheticHandle;
state = createdState;
return true;
}
private static void TracePthreadSelf(CpuContext ctx, ulong currentThreadHandle)
{
if (!ShouldTracePthread())
{
return;
}
var currentThreadId = KernelPthreadState.GetCurrentThreadUniqueId();
Console.Error.WriteLine(
$"[LOADER][TRACE] pthread_self: stale_rdi=0x{ctx[CpuRegister.Rdi]:X16} thread=0x{currentThreadHandle:X16} tid=0x{currentThreadId:X16}");
}
private static void TracePthreadMutex(CpuContext ctx, string operation, ulong mutexAddress, ulong resolvedAddress, PthreadMutexState? state, ulong currentThreadId, int result)
{
if (!ShouldTracePthread())
{
return;
}
_ = ctx.TryReadUInt64(mutexAddress, out var guestWord0);
_ = ctx.TryReadUInt64(mutexAddress + 8, out var guestWord1);
Console.Error.WriteLine(
$"[LOADER][TRACE] pthread_{operation}: mutex=0x{mutexAddress:X16} resolved=0x{resolvedAddress:X16} " +
$"guest[0]=0x{guestWord0:X16} guest[8]=0x{guestWord1:X16} " +
$"current=0x{currentThreadId:X16} owner=0x{(state?.OwnerThreadId ?? 0):X16} " +
$"recursion={(state?.RecursionCount ?? 0)} type={(state?.Type ?? 0)} result=0x{unchecked((uint)result):X8}");
}
private static void TracePthreadCond(string operation, ulong condAddress, ulong mutexAddress, PthreadCondState? state, bool timed, int result)
{
if (!ShouldTracePthread())
{
return;
}
Console.Error.WriteLine(
$"[LOADER][TRACE] pthread_cond_{operation}: cond=0x{condAddress:X16} mutex=0x{mutexAddress:X16} " +
$"waiters={(state?.Waiters ?? 0)} epoch=0x{(state?.SignalEpoch ?? 0):X} timed={timed} result=0x{unchecked((uint)result):X8}");
}
private static bool ShouldTracePthread()
{
return string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_PTHREADS"), "1", StringComparison.Ordinal);
}
}