mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-19 09:26:16 +08:00
[kernel] pthread improvements
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Threading;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
@@ -19,11 +20,15 @@ public static class KernelPthreadCompatExports
|
||||
private const int MutexAttrObjectSize = 0x40;
|
||||
private const int CondObjectSize = 0x100;
|
||||
private const int DefaultSpuriousCondWakeMilliseconds = 1;
|
||||
private const int PthreadOnceUninitialized = 0;
|
||||
private const int PthreadOnceInProgress = 1;
|
||||
private const int PthreadOnceDone = 2;
|
||||
|
||||
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 Dictionary<ulong, object> _onceGates = new();
|
||||
private static readonly HashSet<ulong> _condAttrStates = new();
|
||||
|
||||
private sealed class PthreadMutexState
|
||||
@@ -57,6 +62,13 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "EotR8a3ASf4",
|
||||
ExportName = "pthread_self",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadSelf(CpuContext ctx) => PthreadSelf(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "3PtV6p3QNX4",
|
||||
ExportName = "scePthreadEqual",
|
||||
@@ -340,6 +352,93 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "14bOACANTBo",
|
||||
ExportName = "scePthreadOnce",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PthreadOnce(CpuContext ctx)
|
||||
{
|
||||
var onceAddress = ctx[CpuRegister.Rdi];
|
||||
var initRoutine = ctx[CpuRegister.Rsi];
|
||||
if (onceAddress == 0 || initRoutine == 0)
|
||||
{
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (!TryReadInt32(ctx, onceAddress, out var onceValue))
|
||||
{
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (onceValue == PthreadOnceDone)
|
||||
{
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
var gate = GetPthreadOnceGate(onceAddress);
|
||||
var shouldCall = false;
|
||||
lock (gate)
|
||||
{
|
||||
if (!TryReadInt32(ctx, onceAddress, out onceValue))
|
||||
{
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
while (onceValue == PthreadOnceInProgress)
|
||||
{
|
||||
Monitor.Wait(gate, TimeSpan.FromMilliseconds(1));
|
||||
if (!TryReadInt32(ctx, onceAddress, out onceValue))
|
||||
{
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
if (onceValue != PthreadOnceDone)
|
||||
{
|
||||
if (!TryWriteInt32(ctx, onceAddress, PthreadOnceInProgress))
|
||||
{
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
shouldCall = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCall)
|
||||
{
|
||||
var scheduler = GuestThreadExecution.Scheduler;
|
||||
string? error = null;
|
||||
if (scheduler is null ||
|
||||
!scheduler.TryCallGuestFunction(ctx, initRoutine, 0, 0, 0, 0, "pthread_once", out error))
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
_ = TryWriteInt32(ctx, onceAddress, PthreadOnceUninitialized);
|
||||
Monitor.PulseAll(gate);
|
||||
}
|
||||
|
||||
TracePthreadOnce(onceAddress, initRoutine, "failed", error);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
}
|
||||
|
||||
lock (gate)
|
||||
{
|
||||
if (!TryWriteInt32(ctx, onceAddress, PthreadOnceDone))
|
||||
{
|
||||
_ = TryWriteInt32(ctx, onceAddress, PthreadOnceUninitialized);
|
||||
Monitor.PulseAll(gate);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
Monitor.PulseAll(gate);
|
||||
}
|
||||
}
|
||||
|
||||
TracePthreadOnce(onceAddress, initRoutine, shouldCall ? "call" : "done", null);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static int PthreadMutexInitCore(CpuContext ctx, ulong mutexAddress, ulong attrAddress)
|
||||
{
|
||||
if (mutexAddress == 0)
|
||||
@@ -1136,6 +1235,46 @@ public static class KernelPthreadCompatExports
|
||||
};
|
||||
}
|
||||
|
||||
private static object GetPthreadOnceGate(ulong onceAddress)
|
||||
{
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (!_onceGates.TryGetValue(onceAddress, out var gate))
|
||||
{
|
||||
gate = new object();
|
||||
_onceGates[onceAddress] = gate;
|
||||
}
|
||||
|
||||
return gate;
|
||||
}
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, OrbisGen2Result result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
private static bool TryReadInt32(CpuContext ctx, ulong address, out int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static bool CreateImplicitMutexState(CpuContext ctx, ulong mutexAddress, int type, out ulong resolvedAddress, [NotNullWhen(true)] out PthreadMutexState? state)
|
||||
{
|
||||
var createdState = new PthreadMutexState
|
||||
@@ -1204,6 +1343,18 @@ public static class KernelPthreadCompatExports
|
||||
$"[LOADER][TRACE] pthread_self: stale_rdi=0x{ctx[CpuRegister.Rdi]:X16} thread=0x{currentThreadHandle:X16} tid=0x{currentThreadId:X16}");
|
||||
}
|
||||
|
||||
private static void TracePthreadOnce(ulong onceAddress, ulong initRoutine, string operation, string? error)
|
||||
{
|
||||
if (!ShouldTracePthread())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var suffix = string.IsNullOrWhiteSpace(error) ? string.Empty : $" error={error}";
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][TRACE] pthread_once_{operation}: once=0x{onceAddress:X16} init=0x{initRoutine:X16}{suffix}");
|
||||
}
|
||||
|
||||
private static void TracePthreadMutex(CpuContext ctx, string operation, ulong mutexAddress, ulong resolvedAddress, PthreadMutexState? state, ulong currentThreadId, int result)
|
||||
{
|
||||
if (!ShouldTracePthread())
|
||||
|
||||
@@ -185,6 +185,35 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "rcrVFJsQWRY",
|
||||
ExportName = "scePthreadGetaffinity",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PthreadGetaffinity(CpuContext ctx)
|
||||
{
|
||||
var thread = ctx[CpuRegister.Rdi];
|
||||
var outMaskAddress = ctx[CpuRegister.Rsi];
|
||||
if (thread == 0 || outMaskAddress == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ulong affinityMask;
|
||||
lock (_stateGate)
|
||||
{
|
||||
affinityMask = GetOrCreateThreadStateLocked(thread).AffinityMask;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt64(outMaskAddress, affinityMask))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "1tKyG7RlMJo",
|
||||
ExportName = "scePthreadGetprio",
|
||||
|
||||
@@ -106,6 +106,8 @@ public static class KernelRuntimeCompatExports
|
||||
}
|
||||
|
||||
var rbx = ctx[CpuRegister.Rbx];
|
||||
var r12 = ctx[CpuRegister.R12];
|
||||
var r13 = ctx[CpuRegister.R13];
|
||||
var lockAddress = rbx == 0 ? 0 : rbx + 0xF78;
|
||||
var lockText = "unreadable";
|
||||
if (lockAddress != 0 && ctx.TryReadUInt64(lockAddress, out var lockValue))
|
||||
@@ -113,6 +115,25 @@ public static class KernelRuntimeCompatExports
|
||||
lockText = $"0x{lockValue:X16}";
|
||||
}
|
||||
|
||||
var schedulerText = "unreadable";
|
||||
if (r12 != 0 && ctx.TryReadUInt64(r12 + 8, out var schedulerAddress))
|
||||
{
|
||||
schedulerText = $"0x{schedulerAddress:X16}";
|
||||
}
|
||||
|
||||
var waitValueText = "unreadable";
|
||||
if (r13 != 0 && ctx.TryReadUInt64(r13, out var waitValue))
|
||||
{
|
||||
waitValueText = $"0x{waitValue:X16}";
|
||||
}
|
||||
|
||||
var callerReturnText = "unreadable";
|
||||
var rbp = ctx[CpuRegister.Rbp];
|
||||
if (rbp != 0 && ctx.TryReadUInt64(rbp + 8, out var callerReturn))
|
||||
{
|
||||
callerReturnText = $"0x{callerReturn:X16}";
|
||||
}
|
||||
|
||||
var returnRip = GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame)
|
||||
? frame.ReturnRip
|
||||
: 0UL;
|
||||
@@ -120,7 +141,7 @@ public static class KernelRuntimeCompatExports
|
||||
var fiber = FiberExports.GetCurrentFiberAddressForDiagnostics(ctx);
|
||||
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][TRACE] usleep#{count}: usec={micros} ret=0x{returnRip:X16} thread=0x{thread:X16} fiber=0x{fiber:X16} rbx=0x{rbx:X16} lock@+F78=0x{lockAddress:X16}:{lockText} r13=0x{ctx[CpuRegister.R13]:X16} r14=0x{ctx[CpuRegister.R14]:X16} r15=0x{ctx[CpuRegister.R15]:X16}");
|
||||
$"[LOADER][TRACE] usleep#{count}: usec={micros} ret=0x{returnRip:X16} caller={callerReturnText} thread=0x{thread:X16} fiber=0x{fiber:X16} rbx=0x{rbx:X16} lock@+F78=0x{lockAddress:X16}:{lockText} r12=0x{r12:X16} scheduler@+8={schedulerText} r13=0x{r13:X16}:{waitValueText} r14=0x{ctx[CpuRegister.R14]:X16} r15=0x{ctx[CpuRegister.R15]:X16}");
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
|
||||
Reference in New Issue
Block a user