mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-05 17:39:53 +08:00
[kernel] carry pthread scheduling
This commit is contained in:
@@ -347,6 +347,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
|
|
||||||
public string Name { get; init; } = string.Empty;
|
public string Name { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public int Priority { get; init; }
|
||||||
|
|
||||||
|
public ulong AffinityMask { get; init; }
|
||||||
|
|
||||||
public CpuContext Context { get; init; } = null!;
|
public CpuContext Context { get; init; } = null!;
|
||||||
|
|
||||||
public GuestThreadRunState State { get; set; }
|
public GuestThreadRunState State { get; set; }
|
||||||
@@ -388,14 +392,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
private Action? _work;
|
private Action? _work;
|
||||||
private volatile bool _stopping;
|
private volatile bool _stopping;
|
||||||
|
|
||||||
public GuestContinuationRunner(ulong guestThreadHandle)
|
public GuestContinuationRunner(ulong guestThreadHandle, ThreadPriority priority)
|
||||||
{
|
{
|
||||||
_guestThreadHandle = guestThreadHandle;
|
_guestThreadHandle = guestThreadHandle;
|
||||||
_thread = new Thread(ThreadMain)
|
_thread = new Thread(ThreadMain)
|
||||||
{
|
{
|
||||||
IsBackground = true,
|
IsBackground = true,
|
||||||
Name = $"GuestContinuation-{guestThreadHandle:X}",
|
Name = $"GuestContinuation-{guestThreadHandle:X}",
|
||||||
Priority = ThreadPriority.BelowNormal,
|
Priority = priority,
|
||||||
};
|
};
|
||||||
_thread.Start();
|
_thread.Start();
|
||||||
}
|
}
|
||||||
@@ -2393,7 +2397,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
Interlocked.Increment(ref _readyGuestThreadCount);
|
Interlocked.Increment(ref _readyGuestThreadCount);
|
||||||
}
|
}
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[LOADER][INFO] Scheduled guest thread '{thread.Name}' handle=0x{thread.ThreadHandle:X16} entry=0x{thread.EntryPoint:X16} arg=0x{thread.Argument:X16}");
|
$"[LOADER][INFO] Scheduled guest thread '{thread.Name}' handle=0x{thread.ThreadHandle:X16} " +
|
||||||
|
$"entry=0x{thread.EntryPoint:X16} arg=0x{thread.Argument:X16} priority={thread.Priority} " +
|
||||||
|
$"host_priority={MapGuestThreadPriority(thread.Priority)} affinity=0x{thread.AffinityMask:X}");
|
||||||
Pump(creatorContext, "pthread_create");
|
Pump(creatorContext, "pthread_create");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2448,7 +2454,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
{
|
{
|
||||||
IsBackground = true,
|
IsBackground = true,
|
||||||
Name = $"SharpEmu-{thread.Name}",
|
Name = $"SharpEmu-{thread.Name}",
|
||||||
Priority = ThreadPriority.BelowNormal,
|
Priority = MapGuestThreadPriority(thread.Priority),
|
||||||
};
|
};
|
||||||
lock (_guestThreadGate)
|
lock (_guestThreadGate)
|
||||||
{
|
{
|
||||||
@@ -2851,7 +2857,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
{
|
{
|
||||||
if (_guestThreads.TryGetValue(currentGuestThreadHandle, out var guestThread))
|
if (_guestThreads.TryGetValue(currentGuestThreadHandle, out var guestThread))
|
||||||
{
|
{
|
||||||
runner = guestThread.ContinuationRunner ??= new GuestContinuationRunner(currentGuestThreadHandle);
|
runner = guestThread.ContinuationRunner ??= new GuestContinuationRunner(
|
||||||
|
currentGuestThreadHandle,
|
||||||
|
MapGuestThreadPriority(guestThread.Priority));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2990,6 +2998,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
EntryPoint = request.EntryPoint,
|
EntryPoint = request.EntryPoint,
|
||||||
Argument = request.Argument,
|
Argument = request.Argument,
|
||||||
Name = string.IsNullOrWhiteSpace(request.Name) ? $"Thread-{request.ThreadHandle:X}" : request.Name,
|
Name = string.IsNullOrWhiteSpace(request.Name) ? $"Thread-{request.ThreadHandle:X}" : request.Name,
|
||||||
|
Priority = request.Priority,
|
||||||
|
AffinityMask = request.AffinityMask,
|
||||||
Context = context,
|
Context = context,
|
||||||
State = GuestThreadRunState.Ready,
|
State = GuestThreadRunState.Ready,
|
||||||
};
|
};
|
||||||
@@ -3129,11 +3139,77 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
context.TryWriteUInt64(tlsBase + 0x60, tlsBase);
|
context.TryWriteUInt64(tlsBase + 0x60, tlsBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ThreadPriority MapGuestThreadPriority(int priority)
|
||||||
|
{
|
||||||
|
if (priority <= 478)
|
||||||
|
{
|
||||||
|
return ThreadPriority.Highest;
|
||||||
|
}
|
||||||
|
if (priority >= 733)
|
||||||
|
{
|
||||||
|
return ThreadPriority.Lowest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ThreadPriority.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyGuestThreadAffinity(ulong guestAffinityMask)
|
||||||
|
{
|
||||||
|
var hostAffinityMask = MapGuestThreadAffinity(guestAffinityMask);
|
||||||
|
if (hostAffinityMask == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SetThreadAffinityMask(GetCurrentThread(), (nuint)hostAffinityMask) == 0 && _logGuestThreads)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][WARN] Failed to set guest thread affinity guest=0x{guestAffinityMask:X} " +
|
||||||
|
$"host=0x{hostAffinityMask:X} error={Marshal.GetLastWin32Error()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong MapGuestThreadAffinity(ulong guestAffinityMask)
|
||||||
|
{
|
||||||
|
if (guestAffinityMask == 0 || guestAffinityMask == ulong.MaxValue)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var processorCount = Math.Min(Environment.ProcessorCount, 64);
|
||||||
|
if (processorCount == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong hostAffinityMask = 0;
|
||||||
|
for (var guestCpu = 0; guestCpu < 64; guestCpu++)
|
||||||
|
{
|
||||||
|
if ((guestAffinityMask & (1UL << guestCpu)) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostCpu = processorCount < 8
|
||||||
|
? guestCpu % processorCount
|
||||||
|
: processorCount >= 16
|
||||||
|
? guestCpu * 2
|
||||||
|
: guestCpu;
|
||||||
|
if (hostCpu < processorCount)
|
||||||
|
{
|
||||||
|
hostAffinityMask |= 1UL << hostCpu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostAffinityMask;
|
||||||
|
}
|
||||||
|
|
||||||
private void RunGuestThread(GuestThreadState thread, string reason)
|
private void RunGuestThread(GuestThreadState thread, string reason)
|
||||||
{
|
{
|
||||||
var previousLastError = LastError;
|
var previousLastError = LastError;
|
||||||
var previousGuestThreadHandle = GuestThreadExecution.EnterGuestThread(thread.ThreadHandle);
|
var previousGuestThreadHandle = GuestThreadExecution.EnterGuestThread(thread.ThreadHandle);
|
||||||
var previousGuestThreadState = _activeGuestThreadState;
|
var previousGuestThreadState = _activeGuestThreadState;
|
||||||
|
ApplyGuestThreadAffinity(thread.AffinityMask);
|
||||||
Volatile.Write(ref thread.HostThreadId, unchecked((int)GetCurrentThreadId()));
|
Volatile.Write(ref thread.HostThreadId, unchecked((int)GetCurrentThreadId()));
|
||||||
_activeGuestThreadState = thread;
|
_activeGuestThreadState = thread;
|
||||||
try
|
try
|
||||||
@@ -3183,6 +3259,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
case GuestNativeCallExitReason.Blocked:
|
case GuestNativeCallExitReason.Blocked:
|
||||||
thread.State = GuestThreadRunState.Blocked;
|
thread.State = GuestThreadRunState.Blocked;
|
||||||
thread.BlockReason = blockReason;
|
thread.BlockReason = blockReason;
|
||||||
|
if (thread.HasBlockedContinuation &&
|
||||||
|
thread.BlockWakeHandler is not null &&
|
||||||
|
thread.BlockWakeHandler())
|
||||||
|
{
|
||||||
|
thread.State = GuestThreadRunState.Ready;
|
||||||
|
thread.BlockReason = null;
|
||||||
|
thread.BlockWakeHandler = null;
|
||||||
|
thread.BlockDeadlineTimestamp = 0;
|
||||||
|
_readyGuestThreads.Enqueue(thread);
|
||||||
|
Interlocked.Increment(ref _readyGuestThreadCount);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
thread.State = GuestThreadRunState.Faulted;
|
thread.State = GuestThreadRunState.Faulted;
|
||||||
@@ -4144,6 +4231,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll")]
|
||||||
private static extern uint GetCurrentThreadId();
|
private static extern uint GetCurrentThreadId();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern nint GetCurrentThread();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
private static extern nuint SetThreadAffinityMask(nint hThread, nuint dwThreadAffinityMask);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
private static extern nint OpenThread(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId);
|
private static extern nint OpenThread(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ public readonly record struct GuestThreadStartRequest(
|
|||||||
ulong EntryPoint,
|
ulong EntryPoint,
|
||||||
ulong Argument,
|
ulong Argument,
|
||||||
ulong AttributeAddress,
|
ulong AttributeAddress,
|
||||||
string Name);
|
string Name,
|
||||||
|
int Priority,
|
||||||
|
ulong AffinityMask);
|
||||||
|
|
||||||
public readonly record struct GuestThreadSnapshot(
|
public readonly record struct GuestThreadSnapshot(
|
||||||
ulong ThreadHandle,
|
ulong ThreadHandle,
|
||||||
|
|||||||
@@ -204,6 +204,16 @@ public static class KernelExports
|
|||||||
var nameAddress = ctx[CpuRegister.R8];
|
var nameAddress = ctx[CpuRegister.R8];
|
||||||
var name = nameAddress == 0 ? string.Empty : ReadCString(ctx, nameAddress, 256);
|
var name = nameAddress == 0 ? string.Empty : ReadCString(ctx, nameAddress, 256);
|
||||||
var threadHandle = KernelPthreadState.CreateThreadHandle(name);
|
var threadHandle = KernelPthreadState.CreateThreadHandle(name);
|
||||||
|
KernelPthreadExtendedCompatExports.GetThreadStartScheduling(
|
||||||
|
ctx,
|
||||||
|
attrAddress,
|
||||||
|
out var priority,
|
||||||
|
out var affinityMask);
|
||||||
|
KernelPthreadExtendedCompatExports.RegisterThreadStart(
|
||||||
|
threadHandle,
|
||||||
|
name,
|
||||||
|
priority,
|
||||||
|
affinityMask);
|
||||||
if (threadIdAddress != 0 && !ctx.TryWriteUInt64(threadIdAddress, threadHandle))
|
if (threadIdAddress != 0 && !ctx.TryWriteUInt64(threadIdAddress, threadHandle))
|
||||||
{
|
{
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
@@ -212,13 +222,22 @@ public static class KernelExports
|
|||||||
if (ShouldTracePthread())
|
if (ShouldTracePthread())
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[LOADER][TRACE] pthread_create: out=0x{threadIdAddress:X16} attr=0x{attrAddress:X16} entry=0x{entryAddress:X16} arg=0x{argument:X16} name_ptr=0x{nameAddress:X16} name='{name}' -> thread=0x{threadHandle:X16}");
|
$"[LOADER][TRACE] pthread_create: out=0x{threadIdAddress:X16} attr=0x{attrAddress:X16} " +
|
||||||
|
$"entry=0x{entryAddress:X16} arg=0x{argument:X16} name_ptr=0x{nameAddress:X16} " +
|
||||||
|
$"name='{name}' priority={priority} affinity=0x{affinityMask:X} -> thread=0x{threadHandle:X16}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var scheduler = GuestThreadExecution.Scheduler;
|
var scheduler = GuestThreadExecution.Scheduler;
|
||||||
if (scheduler is not null && entryAddress != 0)
|
if (scheduler is not null && entryAddress != 0)
|
||||||
{
|
{
|
||||||
var request = new GuestThreadStartRequest(threadHandle, entryAddress, argument, attrAddress, name);
|
var request = new GuestThreadStartRequest(
|
||||||
|
threadHandle,
|
||||||
|
entryAddress,
|
||||||
|
argument,
|
||||||
|
attrAddress,
|
||||||
|
name,
|
||||||
|
priority,
|
||||||
|
affinityMask);
|
||||||
if (!scheduler.TryStartThread(ctx, request, out var error))
|
if (!scheduler.TryStartThread(ctx, request, out var error))
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ namespace SharpEmu.Libs.Kernel;
|
|||||||
public static class KernelPthreadExtendedCompatExports
|
public static class KernelPthreadExtendedCompatExports
|
||||||
{
|
{
|
||||||
private const int DefaultThreadPriority = 700;
|
private const int DefaultThreadPriority = 700;
|
||||||
private const ulong DefaultThreadAffinityMask = ulong.MaxValue;
|
private const ulong DefaultThreadAffinityMask = 0x7FUL;
|
||||||
private const int DefaultDetachState = 0;
|
private const int DefaultDetachState = 0;
|
||||||
private const ulong DefaultGuardSize = 0x1000UL;
|
private const ulong DefaultGuardSize = 0x1000UL;
|
||||||
private const ulong DefaultStackSize = 0x1_00000UL;
|
private const ulong DefaultStackSize = 0x1_00000UL;
|
||||||
private const int DefaultInheritSched = 0;
|
private const int DefaultInheritSched = 4;
|
||||||
private const int DefaultSchedPolicy = 0;
|
private const int DefaultSchedPolicy = 1;
|
||||||
private const int DefaultSchedPriority = 0;
|
private const int DefaultSchedPriority = DefaultThreadPriority;
|
||||||
private const ulong SyntheticRwlockHandleBase = 0x00006003_0000_0000;
|
private const ulong SyntheticRwlockHandleBase = 0x00006003_0000_0000;
|
||||||
private const ulong SyntheticPthreadAttrHandleBase = 0x00006004_0000_0000;
|
private const ulong SyntheticPthreadAttrHandleBase = 0x00006004_0000_0000;
|
||||||
|
|
||||||
@@ -34,6 +34,48 @@ public static class KernelPthreadExtendedCompatExports
|
|||||||
|
|
||||||
private static readonly ConcurrentDictionary<ulong, ConcurrentDictionary<int, ulong>> _threadLocalSpecific = new();
|
private static readonly ConcurrentDictionary<ulong, ConcurrentDictionary<int, ulong>> _threadLocalSpecific = new();
|
||||||
|
|
||||||
|
internal static void GetThreadStartScheduling(
|
||||||
|
CpuContext ctx,
|
||||||
|
ulong attrAddress,
|
||||||
|
out int priority,
|
||||||
|
out ulong affinityMask)
|
||||||
|
{
|
||||||
|
if (attrAddress == 0)
|
||||||
|
{
|
||||||
|
priority = DefaultThreadPriority;
|
||||||
|
affinityMask = DefaultThreadAffinityMask;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolvedAddress = ResolvePthreadAttrHandle(ctx, attrAddress);
|
||||||
|
lock (_stateGate)
|
||||||
|
{
|
||||||
|
var attributes = GetOrCreateAttrStateLocked(resolvedAddress);
|
||||||
|
priority = attributes.SchedPriority;
|
||||||
|
affinityMask = attributes.AffinityMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void RegisterThreadStart(
|
||||||
|
ulong thread,
|
||||||
|
string name,
|
||||||
|
int priority,
|
||||||
|
ulong affinityMask)
|
||||||
|
{
|
||||||
|
lock (_stateGate)
|
||||||
|
{
|
||||||
|
var state = GetOrCreateThreadStateLocked(thread);
|
||||||
|
state.Name = name;
|
||||||
|
state.Priority = priority;
|
||||||
|
state.AffinityMask = affinityMask;
|
||||||
|
state.Attributes = state.Attributes with
|
||||||
|
{
|
||||||
|
SchedPriority = priority,
|
||||||
|
AffinityMask = affinityMask,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class ThreadState
|
private sealed class ThreadState
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|||||||
Reference in New Issue
Block a user