[kernel] carry pthread scheduling

This commit is contained in:
ParantezTech
2026-06-29 13:27:59 +03:00
parent e28259a99d
commit 7f85971e39
4 changed files with 168 additions and 12 deletions
@@ -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);
+3 -1
View File
@@ -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,
+21 -2
View File
@@ -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;