From 7f85971e3996d4684991c03e88169e2e079ffdd6 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Mon, 29 Jun 2026 13:27:59 +0300 Subject: [PATCH] [kernel] carry pthread scheduling --- .../Cpu/Native/DirectExecutionBackend.cs | 103 +++++++++++++++++- src/SharpEmu.HLE/GuestThreadExecution.cs | 4 +- src/SharpEmu.Libs/Kernel/KernelExports.cs | 23 +++- .../KernelPthreadExtendedCompatExports.cs | 50 ++++++++- 4 files changed, 168 insertions(+), 12 deletions(-) diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs index 688cf25..9e719f4 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs @@ -347,6 +347,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I public string Name { get; init; } = string.Empty; + public int Priority { get; init; } + + public ulong AffinityMask { get; init; } + public CpuContext Context { get; init; } = null!; public GuestThreadRunState State { get; set; } @@ -388,14 +392,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private Action? _work; private volatile bool _stopping; - public GuestContinuationRunner(ulong guestThreadHandle) + public GuestContinuationRunner(ulong guestThreadHandle, ThreadPriority priority) { _guestThreadHandle = guestThreadHandle; _thread = new Thread(ThreadMain) { IsBackground = true, Name = $"GuestContinuation-{guestThreadHandle:X}", - Priority = ThreadPriority.BelowNormal, + Priority = priority, }; _thread.Start(); } @@ -2393,7 +2397,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I Interlocked.Increment(ref _readyGuestThreadCount); } 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"); return true; } @@ -2448,7 +2454,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I { IsBackground = true, Name = $"SharpEmu-{thread.Name}", - Priority = ThreadPriority.BelowNormal, + Priority = MapGuestThreadPriority(thread.Priority), }; lock (_guestThreadGate) { @@ -2851,7 +2857,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I { if (_guestThreads.TryGetValue(currentGuestThreadHandle, out var guestThread)) { - runner = guestThread.ContinuationRunner ??= new GuestContinuationRunner(currentGuestThreadHandle); + runner = guestThread.ContinuationRunner ??= new GuestContinuationRunner( + currentGuestThreadHandle, + MapGuestThreadPriority(guestThread.Priority)); } else { @@ -2990,6 +2998,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I EntryPoint = request.EntryPoint, Argument = request.Argument, Name = string.IsNullOrWhiteSpace(request.Name) ? $"Thread-{request.ThreadHandle:X}" : request.Name, + Priority = request.Priority, + AffinityMask = request.AffinityMask, Context = context, State = GuestThreadRunState.Ready, }; @@ -3129,11 +3139,77 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I 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) { var previousLastError = LastError; var previousGuestThreadHandle = GuestThreadExecution.EnterGuestThread(thread.ThreadHandle); var previousGuestThreadState = _activeGuestThreadState; + ApplyGuestThreadAffinity(thread.AffinityMask); Volatile.Write(ref thread.HostThreadId, unchecked((int)GetCurrentThreadId())); _activeGuestThreadState = thread; try @@ -3183,6 +3259,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I case GuestNativeCallExitReason.Blocked: thread.State = GuestThreadRunState.Blocked; 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; default: thread.State = GuestThreadRunState.Faulted; @@ -4144,6 +4231,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I [DllImport("kernel32.dll")] 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)] private static extern nint OpenThread(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId); diff --git a/src/SharpEmu.HLE/GuestThreadExecution.cs b/src/SharpEmu.HLE/GuestThreadExecution.cs index 87bfc2e..28e6efa 100644 --- a/src/SharpEmu.HLE/GuestThreadExecution.cs +++ b/src/SharpEmu.HLE/GuestThreadExecution.cs @@ -10,7 +10,9 @@ public readonly record struct GuestThreadStartRequest( ulong EntryPoint, ulong Argument, ulong AttributeAddress, - string Name); + string Name, + int Priority, + ulong AffinityMask); public readonly record struct GuestThreadSnapshot( ulong ThreadHandle, diff --git a/src/SharpEmu.Libs/Kernel/KernelExports.cs b/src/SharpEmu.Libs/Kernel/KernelExports.cs index ab66074..6f2c709 100644 --- a/src/SharpEmu.Libs/Kernel/KernelExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelExports.cs @@ -204,6 +204,16 @@ public static class KernelExports var nameAddress = ctx[CpuRegister.R8]; var name = nameAddress == 0 ? string.Empty : ReadCString(ctx, nameAddress, 256); 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)) { return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT; @@ -212,13 +222,22 @@ public static class KernelExports if (ShouldTracePthread()) { 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; 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)) { Console.Error.WriteLine( diff --git a/src/SharpEmu.Libs/Kernel/KernelPthreadExtendedCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelPthreadExtendedCompatExports.cs index c3b29bb..9ccaaa5 100644 --- a/src/SharpEmu.Libs/Kernel/KernelPthreadExtendedCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelPthreadExtendedCompatExports.cs @@ -13,13 +13,13 @@ namespace SharpEmu.Libs.Kernel; public static class KernelPthreadExtendedCompatExports { private const int DefaultThreadPriority = 700; - private const ulong DefaultThreadAffinityMask = ulong.MaxValue; + private const ulong DefaultThreadAffinityMask = 0x7FUL; private const int DefaultDetachState = 0; private const ulong DefaultGuardSize = 0x1000UL; private const ulong DefaultStackSize = 0x1_00000UL; - private const int DefaultInheritSched = 0; - private const int DefaultSchedPolicy = 0; - private const int DefaultSchedPriority = 0; + private const int DefaultInheritSched = 4; + private const int DefaultSchedPolicy = 1; + private const int DefaultSchedPriority = DefaultThreadPriority; private const ulong SyntheticRwlockHandleBase = 0x00006003_0000_0000; private const ulong SyntheticPthreadAttrHandleBase = 0x00006004_0000_0000; @@ -34,6 +34,48 @@ public static class KernelPthreadExtendedCompatExports private static readonly ConcurrentDictionary> _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 { public string Name { get; set; } = string.Empty;