Rewrite scheduler context switch code (#1786)

* Rewrite scheduler context switch code

* Fix race in UnmapIpcRestorePermission

* Fix thread exit issue that could leave the scheduler in a invalid state

* Change context switch method to not wait on guest thread, remove spin wait, use SignalAndWait to pass control

* Remove multi-core setting (it is always on now)

* Re-enable assert

* Remove multicore from default config and schema

* Fix race in KTimeManager
This commit is contained in:
gdkchan 2020-12-09 19:20:05 -03:00 committed by GitHub
parent 3484265d37
commit 48278905d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1080 additions and 1160 deletions

View file

@ -198,11 +198,6 @@ namespace Ryujinx.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> EnableDockedMode { get; private set; } public ReactiveObject<bool> EnableDockedMode { get; private set; }
/// <summary>
/// Enables or disables multi-core scheduling of threads
/// </summary>
public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
/// <summary> /// <summary>
/// Enables or disables profiled translation cache persistency /// Enables or disables profiled translation cache persistency
/// </summary> /// </summary>
@ -235,7 +230,6 @@ namespace Ryujinx.Configuration
TimeZone = new ReactiveObject<string>(); TimeZone = new ReactiveObject<string>();
SystemTimeOffset = new ReactiveObject<long>(); SystemTimeOffset = new ReactiveObject<long>();
EnableDockedMode = new ReactiveObject<bool>(); EnableDockedMode = new ReactiveObject<bool>();
EnableMulticoreScheduling = new ReactiveObject<bool>();
EnablePtc = new ReactiveObject<bool>(); EnablePtc = new ReactiveObject<bool>();
EnableFsIntegrityChecks = new ReactiveObject<bool>(); EnableFsIntegrityChecks = new ReactiveObject<bool>();
FsGlobalAccessLogMode = new ReactiveObject<int>(); FsGlobalAccessLogMode = new ReactiveObject<int>();
@ -414,7 +408,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart = CheckUpdatesOnStart, CheckUpdatesOnStart = CheckUpdatesOnStart,
EnableVsync = Graphics.EnableVsync, EnableVsync = Graphics.EnableVsync,
EnableShaderCache = Graphics.EnableShaderCache, EnableShaderCache = Graphics.EnableShaderCache,
EnableMulticoreScheduling = System.EnableMulticoreScheduling,
EnablePtc = System.EnablePtc, EnablePtc = System.EnablePtc,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
@ -476,7 +469,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart.Value = true; CheckUpdatesOnStart.Value = true;
Graphics.EnableVsync.Value = true; Graphics.EnableVsync.Value = true;
Graphics.EnableShaderCache.Value = true; Graphics.EnableShaderCache.Value = true;
System.EnableMulticoreScheduling.Value = true;
System.EnablePtc.Value = false; System.EnablePtc.Value = false;
System.EnableFsIntegrityChecks.Value = true; System.EnableFsIntegrityChecks.Value = true;
System.FsGlobalAccessLogMode.Value = 0; System.FsGlobalAccessLogMode.Value = 0;
@ -788,7 +780,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;

View file

@ -32,7 +32,7 @@ namespace Ryujinx.Common
public static long TicksPerMillisecond { get; } public static long TicksPerMillisecond { get; }
/// <summary> /// <summary>
/// Gets the number of milliseconds elapsed since the system started. /// Gets the number of ticks elapsed since the system started.
/// </summary> /// </summary>
public static long ElapsedTicks public static long ElapsedTicks
{ {
@ -76,7 +76,7 @@ namespace Ryujinx.Common
TicksPerHour = TicksPerMinute * 60; TicksPerHour = TicksPerMinute * 60;
TicksPerDay = TicksPerHour * 24; TicksPerDay = TicksPerHour * 24;
_ticksToNs = 1000000000.0 / (double)Stopwatch.Frequency; _ticksToNs = 1000000000.0 / Stopwatch.Frequency;
} }
} }
} }

View file

@ -82,9 +82,6 @@ namespace Ryujinx.HLE.HOS
public Keyset KeySet => Device.FileSystem.KeySet; public Keyset KeySet => Device.FileSystem.KeySet;
#pragma warning disable CS0649
private bool _hasStarted;
#pragma warning restore CS0649
private bool _isDisposed; private bool _isDisposed;
public bool EnablePtc { get; set; } public bool EnablePtc { get; set; }
@ -300,22 +297,6 @@ namespace Ryujinx.HLE.HOS
VsyncEvent.ReadableEvent.Signal(); VsyncEvent.ReadableEvent.Signal();
} }
public void EnableMultiCoreScheduling()
{
if (!_hasStarted)
{
KernelContext.Scheduler.MultiCoreScheduling = true;
}
}
public void DisableMultiCoreScheduling()
{
if (!_hasStarted)
{
KernelContext.Scheduler.MultiCoreScheduling = false;
}
}
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
@ -346,9 +327,7 @@ namespace Ryujinx.HLE.HOS
} }
// Exit ourself now! // Exit ourself now!
KernelContext.Scheduler.ExitThread(terminationThread); KernelStatic.GetCurrentThread().Exit();
KernelContext.Scheduler.GetCurrentThread().Exit();
KernelContext.Scheduler.RemoveThread(terminationThread);
}); });
terminationThread.Start(); terminationThread.Start();

View file

@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Common namespace Ryujinx.HLE.HOS.Kernel.Common
@ -47,17 +48,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public void IncrementReferenceCount() public void IncrementReferenceCount()
{ {
Interlocked.Increment(ref _referenceCount); int newRefCount = Interlocked.Increment(ref _referenceCount);
Debug.Assert(newRefCount >= 2);
} }
public void DecrementReferenceCount() public void DecrementReferenceCount()
{ {
if (Interlocked.Decrement(ref _referenceCount) == 0) int newRefCount = Interlocked.Decrement(ref _referenceCount);
Debug.Assert(newRefCount >= 0);
if (newRefCount == 0)
{ {
Destroy(); Destroy();
} }
} }
protected virtual void Destroy() { } protected virtual void Destroy()
{
}
} }
} }

View file

@ -10,9 +10,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
private class WaitingObject private class WaitingObject
{ {
public IKFutureSchedulerObject Object { get; private set; } public IKFutureSchedulerObject Object { get; }
public long TimePoint { get; }
public long TimePoint { get; private set; }
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint) public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
{ {
@ -21,16 +20,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
} }
} }
private List<WaitingObject> _waitingObjects; private readonly KernelContext _context;
private readonly List<WaitingObject> _waitingObjects;
private AutoResetEvent _waitEvent; private AutoResetEvent _waitEvent;
private bool _keepRunning; private bool _keepRunning;
public KTimeManager() public KTimeManager(KernelContext context)
{ {
_context = context;
_waitingObjects = new List<WaitingObject>(); _waitingObjects = new List<WaitingObject>();
_keepRunning = true; _keepRunning = true;
Thread work = new Thread(WaitAndCheckScheduledObjects) Thread work = new Thread(WaitAndCheckScheduledObjects)
@ -45,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
long timePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(timeout); long timePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(timeout);
lock (_waitingObjects) lock (_context.CriticalSection.Lock)
{ {
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint)); _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
} }
@ -53,6 +51,57 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_waitEvent.Set(); _waitEvent.Set();
} }
public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj)
{
lock (_context.CriticalSection.Lock)
{
_waitingObjects.RemoveAll(x => x.Object == schedulerObj);
}
}
private void WaitAndCheckScheduledObjects()
{
using (_waitEvent = new AutoResetEvent(false))
{
while (_keepRunning)
{
WaitingObject next;
lock (_context.CriticalSection.Lock)
{
next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
}
if (next != null)
{
long timePoint = PerformanceCounter.ElapsedMilliseconds;
if (next.TimePoint > timePoint)
{
_waitEvent.WaitOne((int)(next.TimePoint - timePoint));
}
bool timeUp = PerformanceCounter.ElapsedMilliseconds >= next.TimePoint;
if (timeUp)
{
lock (_context.CriticalSection.Lock)
{
if (_waitingObjects.Remove(next))
{
next.Object.TimeUp();
}
}
}
}
else
{
_waitEvent.WaitOne();
}
}
}
}
public static long ConvertNanosecondsToMilliseconds(long time) public static long ConvertNanosecondsToMilliseconds(long time)
{ {
time /= 1000000; time /= 1000000;
@ -70,77 +119,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
return time * 1000000; return time * 1000000;
} }
public static long ConvertMillisecondsToTicks(long time) public static long ConvertHostTicksToTicks(long time)
{ {
return time * 19200; return (long)((time / (double)PerformanceCounter.TicksPerSecond) * 19200000.0);
}
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
{
lock (_waitingObjects)
{
_waitingObjects.RemoveAll(x => x.Object == Object);
}
}
private void WaitAndCheckScheduledObjects()
{
using (_waitEvent = new AutoResetEvent(false))
{
while (_keepRunning)
{
WaitingObject next;
lock (_waitingObjects)
{
next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
}
if (next != null)
{
long timePoint = PerformanceCounter.ElapsedMilliseconds;
if (next.TimePoint > timePoint)
{
_waitEvent.WaitOne((int)(next.TimePoint - timePoint));
}
bool timeUp = PerformanceCounter.ElapsedMilliseconds >= next.TimePoint;
if (timeUp)
{
lock (_waitingObjects)
{
timeUp = _waitingObjects.Remove(next);
}
}
if (timeUp)
{
next.Object.TimeUp();
}
}
else
{
_waitEvent.WaitOne();
}
}
}
} }
public void Dispose() public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{ {
_keepRunning = false; _keepRunning = false;
_waitEvent?.Set(); _waitEvent?.Set();
} }
} }
}
} }

View file

@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
public static bool UserToKernelInt32(KernelContext context, ulong address, out int value) public static bool UserToKernelInt32(KernelContext context, ulong address, out int value)
{ {
KProcess currentProcess = context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) && if (currentProcess.CpuMemory.IsMapped(address) &&
currentProcess.CpuMemory.IsMapped(address + 3)) currentProcess.CpuMemory.IsMapped(address + 3))
@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static bool UserToKernelInt32Array(KernelContext context, ulong address, Span<int> values) public static bool UserToKernelInt32Array(KernelContext context, ulong address, Span<int> values)
{ {
KProcess currentProcess = context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
for (int index = 0; index < values.Length; index++, address += 4) for (int index = 0; index < values.Length; index++, address += 4)
{ {
@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static bool UserToKernelString(KernelContext context, ulong address, int size, out string value) public static bool UserToKernelString(KernelContext context, ulong address, int size, out string value)
{ {
KProcess currentProcess = context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) && if (currentProcess.CpuMemory.IsMapped(address) &&
currentProcess.CpuMemory.IsMapped(address + (ulong)size - 1)) currentProcess.CpuMemory.IsMapped(address + (ulong)size - 1))
@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static bool KernelToUserInt32(KernelContext context, ulong address, int value) public static bool KernelToUserInt32(KernelContext context, ulong address, int value)
{ {
KProcess currentProcess = context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) && if (currentProcess.CpuMemory.IsMapped(address) &&
currentProcess.CpuMemory.IsMapped(address + 3)) currentProcess.CpuMemory.IsMapped(address + 3))
@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static bool KernelToUserInt64(KernelContext context, ulong address, long value) public static bool KernelToUserInt64(KernelContext context, ulong address, long value)
{ {
KProcess currentProcess = context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) && if (currentProcess.CpuMemory.IsMapped(address) &&
currentProcess.CpuMemory.IsMapped(address + 7)) currentProcess.CpuMemory.IsMapped(address + 7))

View file

@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
clientSession = null; clientSession = null;
KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null && if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1)) !currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
clientSession = null; clientSession = null;
KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null && if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1)) !currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))

View file

@ -25,13 +25,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
State = ChannelState.Open; State = ChannelState.Open;
CreatorProcess = context.Scheduler.GetCurrentProcess(); CreatorProcess = KernelStatic.GetCurrentProcess();
CreatorProcess.IncrementReferenceCount(); CreatorProcess.IncrementReferenceCount();
} }
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread currentThread = KernelContext.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize); KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize);
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread currentThread = KernelContext.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent); KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);

View file

@ -214,7 +214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread serverThread = KernelContext.Scheduler.GetCurrentThread(); KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner; KProcess serverProcess = serverThread.Owner;
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
@ -594,7 +594,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread serverThread = KernelContext.Scheduler.GetCurrentThread(); KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner; KProcess serverProcess = serverThread.Owner;
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
@ -889,7 +889,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private MessageHeader GetServerMessageHeader(Message serverMsg) private MessageHeader GetServerMessageHeader(Message serverMsg)
{ {
KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
uint word0 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 0); uint word0 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 0);
uint word1 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 4); uint word1 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 4);

View file

@ -1,6 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
@ -13,6 +12,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KSession(KernelContext context, KClientPort parentPort = null) : base(context) public KSession(KernelContext context, KClientPort parentPort = null) : base(context)
{ {
IncrementReferenceCount();
ServerSession = new KServerSession(context, this); ServerSession = new KServerSession(context, this);
ClientSession = new KClientSession(context, this, parentPort); ClientSession = new KClientSession(context, this, parentPort);

View file

@ -19,6 +19,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool KernelInitialized { get; } public bool KernelInitialized { get; }
public bool Running { get; private set; }
public Switch Device { get; } public Switch Device { get; }
public MemoryBlock Memory { get; } public MemoryBlock Memory { get; }
public Syscall Syscall { get; } public Syscall Syscall { get; }
@ -34,7 +36,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public KSlabHeap UserSlabHeapPages { get; } public KSlabHeap UserSlabHeapPages { get; }
public KCriticalSection CriticalSection { get; } public KCriticalSection CriticalSection { get; }
public KScheduler Scheduler { get; } public KScheduler[] Schedulers { get; }
public KPriorityQueue PriorityQueue { get; }
public KTimeManager TimeManager { get; } public KTimeManager TimeManager { get; }
public KSynchronization Synchronization { get; } public KSynchronization Synchronization { get; }
public KContextIdManager ContextIdManager { get; } public KContextIdManager ContextIdManager { get; }
@ -42,6 +45,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public ConcurrentDictionary<long, KProcess> Processes { get; } public ConcurrentDictionary<long, KProcess> Processes { get; }
public ConcurrentDictionary<string, KAutoObject> AutoObjectNames { get; } public ConcurrentDictionary<string, KAutoObject> AutoObjectNames { get; }
public bool ThreadReselectionRequested { get; set; }
private long _kipId; private long _kipId;
private long _processId; private long _processId;
private long _threadUid; private long _threadUid;
@ -51,6 +56,8 @@ namespace Ryujinx.HLE.HOS.Kernel
Device = device; Device = device;
Memory = memory; Memory = memory;
Running = true;
Syscall = new Syscall(this); Syscall = new Syscall(this);
SyscallHandler = new SyscallHandler(this); SyscallHandler = new SyscallHandler(this);
@ -70,12 +77,18 @@ namespace Ryujinx.HLE.HOS.Kernel
KernelConstants.UserSlabHeapSize); KernelConstants.UserSlabHeapSize);
CriticalSection = new KCriticalSection(this); CriticalSection = new KCriticalSection(this);
Scheduler = new KScheduler(this); Schedulers = new KScheduler[KScheduler.CpuCoresCount];
TimeManager = new KTimeManager(); PriorityQueue = new KPriorityQueue();
TimeManager = new KTimeManager(this);
Synchronization = new KSynchronization(this); Synchronization = new KSynchronization(this);
ContextIdManager = new KContextIdManager(); ContextIdManager = new KContextIdManager();
Scheduler.StartAutoPreemptionThread(); for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
Schedulers[core] = new KScheduler(this, core);
}
StartPreemptionThread();
KernelInitialized = true; KernelInitialized = true;
@ -86,6 +99,16 @@ namespace Ryujinx.HLE.HOS.Kernel
_processId = KernelConstants.InitialProcessId; _processId = KernelConstants.InitialProcessId;
} }
private void StartPreemptionThread()
{
void PreemptionThreadStart()
{
KScheduler.PreemptionThreadLoop(this);
}
new Thread(PreemptionThreadStart) { Name = "HLE.PreemptionThread" }.Start();
}
public long NewThreadUid() public long NewThreadUid()
{ {
return Interlocked.Increment(ref _threadUid) - 1; return Interlocked.Increment(ref _threadUid) - 1;
@ -103,7 +126,13 @@ namespace Ryujinx.HLE.HOS.Kernel
public void Dispose() public void Dispose()
{ {
Scheduler.Dispose(); Running = false;
for (int i = 0; i < KScheduler.CpuCoresCount; i++)
{
Schedulers[i].Dispose();
}
TimeManager.Dispose(); TimeManager.Dispose();
} }
} }

View file

@ -1,6 +1,9 @@
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System; using System;
using System.Threading.Tasks; using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel namespace Ryujinx.HLE.HOS.Kernel
{ {
@ -9,30 +12,52 @@ namespace Ryujinx.HLE.HOS.Kernel
[ThreadStatic] [ThreadStatic]
private static KernelContext Context; private static KernelContext Context;
public static void YieldUntilCompletion(Action action) [ThreadStatic]
private static KThread CurrentThread;
public static KernelResult StartInitialProcess(
KernelContext context,
ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities,
int mainThreadPriority,
ThreadStart customThreadStart)
{ {
YieldUntilCompletion(Task.Factory.StartNew(action)); KProcess process = new KProcess(context);
KernelResult result = process.Initialize(
creationInfo,
capabilities,
context.ResourceLimit,
MemoryRegion.Service,
null,
customThreadStart);
if (result != KernelResult.Success)
{
return result;
} }
public static void YieldUntilCompletion(Task task) process.DefaultCpuCore = 3;
{
KThread currentThread = Context.Scheduler.GetCurrentThread();
Context.CriticalSection.Enter(); context.Processes.TryAdd(process.Pid, process);
currentThread.Reschedule(ThreadSchedState.Paused); return process.Start(mainThreadPriority, 0x1000UL);
task.ContinueWith((antecedent) =>
{
currentThread.Reschedule(ThreadSchedState.Running);
});
Context.CriticalSection.Leave();
} }
internal static void SetKernelContext(KernelContext context) internal static void SetKernelContext(KernelContext context, KThread thread)
{ {
Context = context; Context = context;
CurrentThread = thread;
}
internal static KThread GetCurrentThread()
{
return CurrentThread;
}
internal static KProcess GetCurrentProcess()
{
return GetCurrentThread().Owner;
} }
} }
} }

View file

@ -728,7 +728,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfMemory; return KernelResult.OutOfMemory;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
lock (_blocks) lock (_blocks)
{ {
@ -1225,7 +1225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong remainingPages = remainingSize / PageSize; ulong remainingPages = remainingSize / PageSize;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null && if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Memory, remainingSize)) !currentProcess.ResourceLimit.Reserve(LimitableResource.Memory, remainingSize))
@ -1355,7 +1355,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
PhysicalMemoryUsage -= heapMappedSize; PhysicalMemoryUsage -= heapMappedSize;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
currentProcess.ResourceLimit?.Release(LimitableResource.Memory, heapMappedSize); currentProcess.ResourceLimit?.Release(LimitableResource.Memory, heapMappedSize);
@ -1504,7 +1504,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
attributeMask | MemoryAttribute.Uncached, attributeMask | MemoryAttribute.Uncached,
attributeExpected)) attributeExpected))
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
serverAddress = currentProcess.MemoryManager.GetDramAddressFromVa(serverAddress); serverAddress = currentProcess.MemoryManager.GetDramAddressFromVa(serverAddress);
@ -2111,12 +2111,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
} }
} }
} }
}
InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions); InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions);
return KernelResult.Success; return KernelResult.Success;
} }
}
public KernelResult BorrowIpcBuffer(ulong address, ulong size) public KernelResult BorrowIpcBuffer(ulong address, ulong size)
{ {

View file

@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission) public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission)
{ {
KProcess creator = KernelContext.Scheduler.GetCurrentProcess(); KProcess creator = KernelStatic.GetCurrentProcess();
_creator = creator; _creator = creator;

View file

@ -236,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
if (handle == SelfThreadHandle) if (handle == SelfThreadHandle)
{ {
return _context.Scheduler.GetCurrentThread(); return KernelStatic.GetCurrentThread();
} }
else else
{ {
@ -248,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
if (handle == SelfProcessHandle) if (handle == SelfProcessHandle)
{ {
return _context.Scheduler.GetCurrentProcess(); return KernelStatic.GetCurrentProcess();
} }
else else
{ {

View file

@ -78,6 +78,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public bool IsPaused { get; private set; } public bool IsPaused { get; private set; }
private long _totalTimeRunning;
public long TotalTimeRunning => _totalTimeRunning;
private IProcessContextFactory _contextFactory; private IProcessContextFactory _contextFactory;
public IProcessContext Context { get; private set; } public IProcessContext Context { get; private set; }
public IVirtualMemoryManager CpuMemory => Context.AddressSpace; public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
@ -112,11 +116,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
KPageList pageList, KPageList pageList,
KResourceLimit resourceLimit, KResourceLimit resourceLimit,
MemoryRegion memRegion, MemoryRegion memRegion,
IProcessContextFactory contextFactory) IProcessContextFactory contextFactory,
ThreadStart customThreadStart = null)
{ {
ResourceLimit = resourceLimit; ResourceLimit = resourceLimit;
_memRegion = memRegion; _memRegion = memRegion;
_contextFactory = contextFactory ?? new ProcessContextFactory(); _contextFactory = contextFactory ?? new ProcessContextFactory();
_customThreadStart = customThreadStart;
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift); AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
@ -176,9 +182,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
throw new InvalidOperationException($"Invalid KIP Id {Pid}."); throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
} }
result = ParseProcessInfo(creationInfo); return ParseProcessInfo(creationInfo);
return result;
} }
public KernelResult Initialize( public KernelResult Initialize(
@ -192,6 +196,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ResourceLimit = resourceLimit; ResourceLimit = resourceLimit;
_memRegion = memRegion; _memRegion = memRegion;
_contextFactory = contextFactory ?? new ProcessContextFactory(); _contextFactory = contextFactory ?? new ProcessContextFactory();
_customThreadStart = customThreadStart;
ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion); ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion);
@ -299,8 +304,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
CleanUpForError(); CleanUpForError();
} }
_customThreadStart = customThreadStart;
return result; return result;
} }
@ -751,8 +754,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private void InterruptHandler(object sender, EventArgs e) private void InterruptHandler(object sender, EventArgs e)
{ {
KernelContext.Scheduler.ContextSwitch(); KThread currentThread = KernelStatic.GetCurrentThread();
KernelContext.Scheduler.GetCurrentThread().HandlePostSyscall();
if (currentThread.IsSchedulable)
{
KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
}
currentThread.HandlePostSyscall();
} }
public void IncrementThreadCount() public void IncrementThreadCount()
@ -828,6 +837,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return personalMmHeapPagesCount * KMemoryManager.PageSize; return personalMmHeapPagesCount * KMemoryManager.PageSize;
} }
public void AddCpuTime(long ticks)
{
Interlocked.Add(ref _totalTimeRunning, ticks);
}
public void AddThread(KThread thread) public void AddThread(KThread thread)
{ {
lock (_threadingLock) lock (_threadingLock)
@ -893,7 +907,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
if (shallTerminate) if (shallTerminate)
{ {
UnpauseAndTerminateAllThreadsExcept(KernelContext.Scheduler.GetCurrentThread()); UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
HandleTable.Destroy(); HandleTable.Destroy();
@ -929,7 +943,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
if (shallTerminate) if (shallTerminate)
{ {
UnpauseAndTerminateAllThreadsExcept(KernelContext.Scheduler.GetCurrentThread()); UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
HandleTable.Destroy(); HandleTable.Destroy();
@ -1058,7 +1072,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private bool InvalidAccessHandler(ulong va) private bool InvalidAccessHandler(ulong va)
{ {
KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace(); KernelStatic.GetCurrentThread().PrintGuestStackTrace();
Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}."); Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}.");
@ -1067,7 +1081,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e) private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
{ {
KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace(); KernelStatic.GetCurrentThread().PrintGuestStackTrace();
throw new UndefinedInstructionException(e.Address, e.OpCode); throw new UndefinedInstructionException(e.Address, e.OpCode);
} }

View file

@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetProcessId(int handle, out long pid) public KernelResult GetProcessId(int handle, out long pid)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle); KProcess process = currentProcess.HandleTable.GetKProcess(handle);
@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidThread; return KernelResult.InvalidThread;
} }
KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable; KHandleTable handleTable = KernelStatic.GetCurrentProcess().HandleTable;
KProcess process = new KProcess(_context); KProcess process = new KProcess(_context);
@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle); KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
if (process == null) if (process == null)
{ {
@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.NotFound; return KernelResult.NotFound;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle); KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle);
@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SendSyncRequest(int handle) public KernelResult SendSyncRequest(int handle)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle); KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState; return KernelResult.InvalidMemState;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
@ -303,7 +303,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState; return KernelResult.InvalidMemState;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
@ -363,7 +363,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
serverSessionHandle = 0; serverSessionHandle = 0;
clientSessionHandle = 0; clientSessionHandle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KResourceLimit resourceLimit = currentProcess.ResourceLimit; KResourceLimit resourceLimit = currentProcess.ResourceLimit;
@ -424,7 +424,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
sessionHandle = 0; sessionHandle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle); KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle);
@ -485,7 +485,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded; return KernelResult.MaximumExceeded;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)((long)handlesCount * 4); ulong copySize = (ulong)((long)handlesCount * 4);
@ -513,7 +513,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
handleIndex = 0; handleIndex = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length]; KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
@ -582,7 +582,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded; return KernelResult.MaximumExceeded;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)((long)handlesCount * 4); ulong copySize = (ulong)((long)handlesCount * 4);
@ -681,7 +681,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KPort port = new KPort(_context, maxSessions, isLight, (long)namePtr); KPort port = new KPort(_context, maxSessions, isLight, (long)namePtr);
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle); KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
@ -733,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KPort port = new KPort(_context, maxSessions, false, 0); KPort port = new KPort(_context, maxSessions, false, 0);
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle); KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
@ -756,7 +756,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
clientSessionHandle = 0; clientSessionHandle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle); KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle);
@ -814,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize; return KernelResult.InvalidSize;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.SetHeapSize(size, out position); return process.MemoryManager.SetHeapSize(size, out position);
} }
@ -843,7 +843,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.MemoryManager.SetMemoryAttribute( KernelResult result = process.MemoryManager.SetMemoryAttribute(
position, position,
@ -871,7 +871,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState; return KernelResult.InvalidMemState;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size)) if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{ {
@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange; return KernelResult.InvalidMemRange;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.Map(dst, src, size); return process.MemoryManager.Map(dst, src, size);
} }
@ -907,7 +907,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState; return KernelResult.InvalidMemState;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size)) if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{ {
@ -921,14 +921,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange; return KernelResult.InvalidMemRange;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.Unmap(dst, src, size); return process.MemoryManager.Unmap(dst, src, size);
} }
public KernelResult QueryMemory(ulong infoPtr, ulong position, out ulong pageInfo) public KernelResult QueryMemory(ulong infoPtr, ulong position, out ulong pageInfo)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KMemoryInfo blkInfo = process.MemoryManager.QueryMemory(position); KMemoryInfo blkInfo = process.MemoryManager.QueryMemory(position);
@ -968,7 +968,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission; return KernelResult.InvalidPermission;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle); KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
@ -1009,7 +1009,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState; return KernelResult.InvalidMemState;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle); KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
@ -1056,7 +1056,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission; return KernelResult.InvalidPermission;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KResourceLimit resourceLimit = process.ResourceLimit; KResourceLimit resourceLimit = process.ResourceLimit;
@ -1112,7 +1112,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange; return KernelResult.InvalidMemRange;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0) if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{ {
@ -1125,7 +1125,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange; return KernelResult.InvalidMemRange;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.MapPhysicalMemory(address, size); return process.MemoryManager.MapPhysicalMemory(address, size);
} }
@ -1147,7 +1147,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange; return KernelResult.InvalidMemRange;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0) if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{ {
@ -1160,7 +1160,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange; return KernelResult.InvalidMemRange;
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.UnmapPhysicalMemory(address, size); return process.MemoryManager.UnmapPhysicalMemory(address, size);
} }
@ -1177,7 +1177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize; return KernelResult.InvalidSize;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle); KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@ -1214,7 +1214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize; return KernelResult.InvalidSize;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle); KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@ -1259,7 +1259,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission; return KernelResult.InvalidPermission;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle); KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@ -1285,7 +1285,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult TerminateProcess(int handle) public KernelResult TerminateProcess(int handle)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
process = process.HandleTable.GetObject<KProcess>(handle); process = process.HandleTable.GetObject<KProcess>(handle);
@ -1293,7 +1293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (process != null) if (process != null)
{ {
if (process == _context.Scheduler.GetCurrentProcess()) if (process == KernelStatic.GetCurrentProcess())
{ {
result = KernelResult.Success; result = KernelResult.Success;
process.DecrementToZeroWhileTerminatingCurrent(); process.DecrementToZeroWhileTerminatingCurrent();
@ -1314,12 +1314,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void ExitProcess() public void ExitProcess()
{ {
_context.Scheduler.GetCurrentProcess().TerminateCurrentProcess(); KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
} }
public KernelResult SignalEvent(int handle) public KernelResult SignalEvent(int handle)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle); KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle);
@ -1343,7 +1343,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
KernelResult result; KernelResult result;
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle); KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle);
@ -1363,14 +1363,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult CloseHandle(int handle) public KernelResult CloseHandle(int handle)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle; return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
} }
public KernelResult ResetSignal(int handle) public KernelResult ResetSignal(int handle)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle); KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
@ -1399,12 +1399,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public ulong GetSystemTick() public ulong GetSystemTick()
{ {
return _context.Scheduler.GetCurrentThread().Context.CntpctEl0; return KernelStatic.GetCurrentThread().Context.CntpctEl0;
} }
public void Break(ulong reason) public void Break(ulong reason)
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
if ((reason & (1UL << 31)) == 0) if ((reason & (1UL << 31)) == 0)
{ {
@ -1429,7 +1429,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void OutputDebugString(ulong strPtr, ulong size) public void OutputDebugString(ulong strPtr, ulong size)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
string str = MemoryHelper.ReadAsciiString(process.CpuMemory, (long)strPtr, (long)size); string str = MemoryHelper.ReadAsciiString(process.CpuMemory, (long)strPtr, (long)size);
@ -1466,7 +1466,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle); KProcess process = currentProcess.HandleTable.GetKProcess(handle);
@ -1537,7 +1537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
value = _context.Scheduler.GetCurrentProcess().Debug ? 1 : 0; value = KernelStatic.GetCurrentProcess().Debug ? 1 : 0;
break; break;
} }
@ -1554,7 +1554,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null) if (currentProcess.ResourceLimit != null)
{ {
@ -1581,14 +1581,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle; return KernelResult.InvalidHandle;
} }
int currentCore = _context.Scheduler.GetCurrentThread().CurrentCore; int currentCore = KernelStatic.GetCurrentThread().CurrentCore;
if (subId != -1 && subId != currentCore) if (subId != -1 && subId != currentCore)
{ {
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
value = _context.Scheduler.CoreContexts[currentCore].TotalIdleTimeTicks; value = KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks);
break; break;
} }
@ -1605,8 +1605,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
value = currentProcess.RandomEntropy[subId]; value = currentProcess.RandomEntropy[subId];
@ -1620,14 +1619,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
KThread thread = _context.Scheduler.GetCurrentProcess().HandleTable.GetKThread(handle); KThread thread = KernelStatic.GetCurrentProcess().HandleTable.GetKThread(handle);
if (thread == null) if (thread == null)
{ {
return KernelResult.InvalidHandle; return KernelResult.InvalidHandle;
} }
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
int currentCore = currentThread.CurrentCore; int currentCore = currentThread.CurrentCore;
@ -1636,13 +1635,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success; return KernelResult.Success;
} }
KCoreContext coreContext = _context.Scheduler.CoreContexts[currentCore]; KScheduler scheduler = _context.Schedulers[currentCore];
long timeDelta = PerformanceCounter.ElapsedMilliseconds - coreContext.LastContextSwitchTime; long timeDelta = PerformanceCounter.ElapsedTicks - scheduler.LastContextSwitchTime;
if (subId != -1) if (subId != -1)
{ {
value = KTimeManager.ConvertMillisecondsToTicks(timeDelta); value = KTimeManager.ConvertHostTicksToTicks(timeDelta);
} }
else else
{ {
@ -1653,7 +1652,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
totalTimeRunning += timeDelta; totalTimeRunning += timeDelta;
} }
value = KTimeManager.ConvertMillisecondsToTicks(totalTimeRunning); value = KTimeManager.ConvertHostTicksToTicks(totalTimeRunning);
} }
break; break;
@ -1669,7 +1668,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
KEvent Event = new KEvent(_context); KEvent Event = new KEvent(_context);
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle); KernelResult result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle);
@ -1701,7 +1700,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (maxCount != 0) if (maxCount != 0)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)maxCount * 8; ulong copySize = (ulong)maxCount * 8;
@ -1807,7 +1806,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
handle = 0; handle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (cpuCore == -2) if (cpuCore == -2)
{ {
@ -1844,7 +1843,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (result == KernelResult.Success) if (result == KernelResult.Success)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
result = process.HandleTable.GenerateHandle(thread, out handle); result = process.HandleTable.GenerateHandle(thread, out handle);
} }
@ -1860,7 +1859,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult StartThread(int handle) public KernelResult StartThread(int handle)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -1887,35 +1886,31 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void ExitThread() public void ExitThread()
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
_context.Scheduler.ExitThread(currentThread);
currentThread.Exit(); currentThread.Exit();
} }
public void SleepThread(long timeout) public void SleepThread(long timeout)
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread();
if (timeout < 1) if (timeout < 1)
{ {
switch (timeout) switch (timeout)
{ {
case 0: currentThread.Yield(); break; case 0: KScheduler.Yield(_context); break;
case -1: currentThread.YieldWithLoadBalancing(); break; case -1: KScheduler.YieldWithLoadBalancing(_context); break;
case -2: currentThread.YieldAndWaitForLoadBalancing(); break; case -2: KScheduler.YieldToAnyThread(_context); break;
} }
} }
else else
{ {
currentThread.Sleep(timeout); KernelStatic.GetCurrentThread().Sleep(timeout);
} }
} }
public KernelResult GetThreadPriority(int handle, out int priority) public KernelResult GetThreadPriority(int handle, out int priority)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -1937,7 +1932,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
// TODO: NPDM check. // TODO: NPDM check.
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -1953,7 +1948,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetThreadCoreMask(int handle, out int preferredCore, out long affinityMask) public KernelResult GetThreadCoreMask(int handle, out int preferredCore, out long affinityMask)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -1975,7 +1970,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask) public KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (preferredCore == -2) if (preferredCore == -2)
{ {
@ -2009,7 +2004,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
} }
} }
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -2023,12 +2018,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public int GetCurrentProcessorNumber() public int GetCurrentProcessorNumber()
{ {
return _context.Scheduler.GetCurrentThread().CurrentCore; return KernelStatic.GetCurrentThread().CurrentCore;
} }
public KernelResult GetThreadId(int handle, out long threadUid) public KernelResult GetThreadId(int handle, out long threadUid)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -2048,7 +2043,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SetThreadActivity(int handle, bool pause) public KernelResult SetThreadActivity(int handle, bool pause)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetObject<KThread>(handle); KThread thread = process.HandleTable.GetObject<KThread>(handle);
@ -2062,7 +2057,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle; return KernelResult.InvalidHandle;
} }
if (thread == _context.Scheduler.GetCurrentThread()) if (thread == KernelStatic.GetCurrentThread())
{ {
return KernelResult.InvalidThread; return KernelResult.InvalidThread;
} }
@ -2072,8 +2067,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetThreadContext3(ulong address, int handle) public KernelResult GetThreadContext3(ulong address, int handle)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
KThread thread = currentProcess.HandleTable.GetObject<KThread>(handle); KThread thread = currentProcess.HandleTable.GetObject<KThread>(handle);
@ -2190,13 +2185,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded; return KernelResult.MaximumExceeded;
} }
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects).Slice(0, handlesCount); var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects).Slice(0, handlesCount);
if (handlesCount != 0) if (handlesCount != 0)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr) if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr)
{ {
@ -2267,7 +2262,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult CancelSynchronization(int handle) public KernelResult CancelSynchronization(int handle)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle); KThread thread = process.HandleTable.GetKThread(handle);
@ -2293,7 +2288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress; return KernelResult.InvalidAddress;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle); return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
} }
@ -2310,7 +2305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress; return KernelResult.InvalidAddress;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress); return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
} }
@ -2331,7 +2326,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress; return KernelResult.InvalidAddress;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic( return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
mutexAddress, mutexAddress,
@ -2342,7 +2337,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SignalProcessWideKey(ulong address, int count) public KernelResult SignalProcessWideKey(ulong address, int count)
{ {
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
currentProcess.AddressArbiter.SignalProcessWideKey(address, count); currentProcess.AddressArbiter.SignalProcessWideKey(address, count);
@ -2361,7 +2356,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress; return KernelResult.InvalidAddress;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
return type switch return type switch
{ {
@ -2387,7 +2382,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress; return KernelResult.InvalidAddress;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
return type switch return type switch
{ {

View file

@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private void PostSvcHandler() private void PostSvcHandler()
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.HandlePostSyscall(); currentThread.HandlePostSyscall();
} }

View file

@ -1,66 +0,0 @@
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class HleCoreManager
{
private class PausableThread
{
public ManualResetEvent Event { get; private set; }
public bool IsExiting { get; set; }
public PausableThread()
{
Event = new ManualResetEvent(false);
}
}
private ConcurrentDictionary<Thread, PausableThread> _threads;
public HleCoreManager()
{
_threads = new ConcurrentDictionary<Thread, PausableThread>();
}
public void Set(Thread thread)
{
GetThread(thread).Event.Set();
}
public void Reset(Thread thread)
{
GetThread(thread).Event.Reset();
}
public void Wait(Thread thread)
{
PausableThread pausableThread = GetThread(thread);
if (!pausableThread.IsExiting)
{
pausableThread.Event.WaitOne();
}
}
public void Exit(Thread thread)
{
GetThread(thread).IsExiting = true;
}
private PausableThread GetThread(Thread thread)
{
return _threads.GetOrAdd(thread, (key) => new PausableThread());
}
public void RemoveThread(Thread thread)
{
if (_threads.TryRemove(thread, out PausableThread pausableThread))
{
pausableThread.Event.Set();
pausableThread.Event.Dispose();
}
}
}
}

View file

@ -1,150 +0,0 @@
using System;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
partial class KScheduler
{
private const int RoundRobinTimeQuantumMs = 10;
private int _currentCore;
public bool MultiCoreScheduling { get; set; }
public HleCoreManager CoreManager { get; private set; }
private bool _keepPreempting;
public void StartAutoPreemptionThread()
{
Thread preemptionThread = new Thread(PreemptCurrentThread)
{
Name = "HLE.PreemptionThread"
};
_keepPreempting = true;
preemptionThread.Start();
}
public void ContextSwitch()
{
lock (CoreContexts)
{
if (MultiCoreScheduling)
{
int selectedCount = 0;
for (int core = 0; core < CpuCoresCount; core++)
{
KCoreContext coreContext = CoreContexts[core];
if (coreContext.ContextSwitchNeeded && (coreContext.CurrentThread?.IsCurrentHostThread() ?? false))
{
coreContext.ContextSwitch();
}
if (coreContext.CurrentThread?.IsCurrentHostThread() ?? false)
{
selectedCount++;
}
}
if (selectedCount == 0)
{
CoreManager.Reset(Thread.CurrentThread);
}
else if (selectedCount == 1)
{
CoreManager.Set(Thread.CurrentThread);
}
else
{
throw new InvalidOperationException("Thread scheduled in more than one core!");
}
}
else
{
KThread currentThread = CoreContexts[_currentCore].CurrentThread;
bool hasThreadExecuting = currentThread != null;
if (hasThreadExecuting)
{
// If this is not the thread that is currently executing, we need
// to request an interrupt to allow safely starting another thread.
if (!currentThread.IsCurrentHostThread())
{
currentThread.Context.RequestInterrupt();
return;
}
CoreManager.Reset(currentThread.HostThread);
}
// Advance current core and try picking a thread,
// keep advancing if it is null.
for (int core = 0; core < 4; core++)
{
_currentCore = (_currentCore + 1) % CpuCoresCount;
KCoreContext coreContext = CoreContexts[_currentCore];
coreContext.UpdateCurrentThread();
if (coreContext.CurrentThread != null)
{
CoreManager.Set(coreContext.CurrentThread.HostThread);
coreContext.CurrentThread.Execute();
break;
}
}
// If nothing was running before, then we are on a "external"
// HLE thread, we don't need to wait.
if (!hasThreadExecuting)
{
return;
}
}
}
CoreManager.Wait(Thread.CurrentThread);
}
private void PreemptCurrentThread()
{
// Preempts current thread every 10 milliseconds on a round-robin fashion,
// when multi core scheduling is disabled, to try ensuring that all threads
// gets a chance to run.
while (_keepPreempting)
{
lock (CoreContexts)
{
KThread currentThread = CoreContexts[_currentCore].CurrentThread;
currentThread?.Context.RequestInterrupt();
}
PreemptThreads();
Thread.Sleep(RoundRobinTimeQuantumMs);
}
}
public void ExitThread(KThread thread)
{
thread.Context.StopRunning();
CoreManager.Exit(thread.HostThread);
}
public void RemoveThread(KThread thread)
{
CoreManager.RemoveThread(thread.HostThread);
}
}
}

View file

@ -25,14 +25,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
currentThread.SignaledObj = null; currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.Success; currentThread.ObjSyncResult = KernelResult.Success;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!KernelTransfer.UserToKernelInt32(_context, mutexAddress, out int mutexValue)) if (!KernelTransfer.UserToKernelInt32(_context, mutexAddress, out int mutexValue))
{ {
@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
(KernelResult result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress); (KernelResult result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress);
@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.SignaledObj = null; currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.TimedOut; currentThread.ObjSyncResult = KernelResult.TimedOut;
@ -227,7 +227,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
ulong address = requester.MutexAddress; ulong address = requester.MutexAddress;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address)) if (!currentProcess.CpuMemory.IsMapped(address))
{ {
@ -293,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult WaitForAddressIfEqual(ulong address, int value, long timeout) public KernelResult WaitForAddressIfEqual(ulong address, int value, long timeout)
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
@ -368,7 +368,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
bool shouldDecrement, bool shouldDecrement,
long timeout) long timeout)
{ {
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
@ -383,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.SignaledObj = null; currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.TimedOut; currentThread.ObjSyncResult = KernelResult.TimedOut;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!KernelTransfer.UserToKernelInt32(_context, address, out int currentValue)) if (!KernelTransfer.UserToKernelInt32(_context, address, out int currentValue))
{ {
@ -483,7 +483,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address)) if (!currentProcess.CpuMemory.IsMapped(address))
{ {
@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
offset = 1; offset = 1;
} }
KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address)) if (!currentProcess.CpuMemory.IsMapped(address))
{ {

View file

@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
public static void Wait(KernelContext context, LinkedList<KThread> threadList, object mutex, long timeout) public static void Wait(KernelContext context, LinkedList<KThread> threadList, object mutex, long timeout)
{ {
KThread currentThread = context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter(); context.CriticalSection.Enter();

View file

@ -1,79 +0,0 @@
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KCoreContext
{
private KScheduler _scheduler;
private HleCoreManager _coreManager;
public bool ContextSwitchNeeded { get; private set; }
public long LastContextSwitchTime { get; private set; }
public long TotalIdleTimeTicks { get; private set; } //TODO
public KThread CurrentThread { get; private set; }
public KThread SelectedThread { get; private set; }
public KCoreContext(KScheduler scheduler, HleCoreManager coreManager)
{
_scheduler = scheduler;
_coreManager = coreManager;
}
public void SelectThread(KThread thread)
{
SelectedThread = thread;
if (SelectedThread != CurrentThread)
{
ContextSwitchNeeded = true;
}
}
public void UpdateCurrentThread()
{
ContextSwitchNeeded = false;
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
CurrentThread = SelectedThread;
if (CurrentThread != null)
{
long currentTime = PerformanceCounter.ElapsedMilliseconds;
CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime;
CurrentThread.LastScheduledTime = currentTime;
}
}
public void ContextSwitch()
{
ContextSwitchNeeded = false;
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
if (CurrentThread != null)
{
_coreManager.Reset(CurrentThread.HostThread);
}
CurrentThread = SelectedThread;
if (CurrentThread != null)
{
long currentTime = PerformanceCounter.ElapsedMilliseconds;
CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime;
CurrentThread.LastScheduledTime = currentTime;
_coreManager.Set(CurrentThread.HostThread);
CurrentThread.Execute();
}
}
}
}

View file

@ -5,21 +5,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KCriticalSection class KCriticalSection
{ {
private readonly KernelContext _context; private readonly KernelContext _context;
private readonly object _lock;
public object LockObj { get; private set; }
private int _recursionCount; private int _recursionCount;
public object Lock => _lock;
public KCriticalSection(KernelContext context) public KCriticalSection(KernelContext context)
{ {
_context = context; _context = context;
_lock = new object();
LockObj = new object();
} }
public void Enter() public void Enter()
{ {
Monitor.Enter(LockObj); Monitor.Enter(_lock);
_recursionCount++; _recursionCount++;
} }
@ -31,61 +30,34 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return; return;
} }
bool doContextSwitch = false;
if (--_recursionCount == 0) if (--_recursionCount == 0)
{ {
if (_context.Scheduler.ThreadReselectionRequested) ulong scheduledCoresMask = KScheduler.SelectThreads(_context);
{
_context.Scheduler.SelectThreads();
}
Monitor.Exit(LockObj); Monitor.Exit(_lock);
if (_context.Scheduler.MultiCoreScheduling) KThread currentThread = KernelStatic.GetCurrentThread();
bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable;
if (isCurrentThreadSchedulable)
{ {
lock (_context.Scheduler.CoreContexts) KScheduler.EnableScheduling(_context, scheduledCoresMask);
{
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
KCoreContext coreContext = _context.Scheduler.CoreContexts[core];
if (coreContext.ContextSwitchNeeded)
{
KThread currentThread = coreContext.CurrentThread;
if (currentThread == null)
{
// Nothing is running, we can perform the context switch immediately.
coreContext.ContextSwitch();
}
else if (currentThread.IsCurrentHostThread())
{
// Thread running on the current core, context switch will block.
doContextSwitch = true;
} }
else else
{ {
// Thread running on another core, request a interrupt. KScheduler.EnableSchedulingFromForeignThread(_context, scheduledCoresMask);
currentThread.Context.RequestInterrupt();
} // If the thread exists but is not schedulable, we still want to suspend
} // it if it's not runnable. That allows the kernel to still block HLE threads
// even if they are not scheduled on guest cores.
if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running)
{
currentThread.SchedulerWaitEvent.WaitOne();
} }
} }
} }
else else
{ {
doContextSwitch = true; Monitor.Exit(_lock);
}
}
else
{
Monitor.Exit(LockObj);
}
if (doContextSwitch)
{
_context.Scheduler.ContextSwitch();
} }
} }
} }

View file

@ -1,8 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Threading namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
class KSchedulingData class KPriorityQueue
{ {
private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore; private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore; private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private long[] _scheduledPrioritiesPerCore; private long[] _scheduledPrioritiesPerCore;
private long[] _suggestedPrioritiesPerCore; private long[] _suggestedPrioritiesPerCore;
public KSchedulingData() public KPriorityQueue()
{ {
_suggestedThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][]; _suggestedThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][];
_scheduledThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][]; _scheduledThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][];
@ -45,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
long prioMask = prios[core]; long prioMask = prios[core];
int prio = CountTrailingZeros(prioMask); int prio = BitOperations.TrailingZeroCount(prioMask);
prioMask &= ~(1L << prio); prioMask &= ~(1L << prio);
@ -62,42 +63,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
node = node.Next; node = node.Next;
} }
prio = CountTrailingZeros(prioMask); prio = BitOperations.TrailingZeroCount(prioMask);
prioMask &= ~(1L << prio); prioMask &= ~(1L << prio);
} }
} }
private int CountTrailingZeros(long value)
{
int count = 0;
while (((value >> count) & 0xf) == 0 && count < 64)
{
count += 4;
}
while (((value >> count) & 1) == 0 && count < 64)
{
count++;
}
return count;
}
public void TransferToCore(int prio, int dstCore, KThread thread) public void TransferToCore(int prio, int dstCore, KThread thread)
{ {
bool schedulable = thread.DynamicPriority < KScheduler.PrioritiesCount; int srcCore = thread.ActiveCore;
if (srcCore == dstCore)
int srcCore = thread.CurrentCore;
thread.CurrentCore = dstCore;
if (srcCore == dstCore || !schedulable)
{ {
return; return;
} }
thread.ActiveCore = dstCore;
if (srcCore >= 0) if (srcCore >= 0)
{ {
Unschedule(prio, srcCore, thread); Unschedule(prio, srcCore, thread);
@ -168,13 +149,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_scheduledPrioritiesPerCore[core] |= 1L << prio; _scheduledPrioritiesPerCore[core] |= 1L << prio;
} }
public void Reschedule(int prio, int core, KThread thread) public KThread Reschedule(int prio, int core, KThread thread)
{ {
if (prio >= KScheduler.PrioritiesCount)
{
return null;
}
LinkedList<KThread> queue = ScheduledQueue(prio, core); LinkedList<KThread> queue = ScheduledQueue(prio, core);
queue.Remove(thread.SiblingsPerCore[core]); queue.Remove(thread.SiblingsPerCore[core]);
thread.SiblingsPerCore[core] = queue.AddLast(thread); thread.SiblingsPerCore[core] = queue.AddLast(thread);
return queue.First.Value;
} }
public void Unschedule(int prio, int core, KThread thread) public void Unschedule(int prio, int core, KThread thread)

View file

@ -1,7 +1,10 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
@ -10,130 +13,88 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public const int PrioritiesCount = 64; public const int PrioritiesCount = 64;
public const int CpuCoresCount = 4; public const int CpuCoresCount = 4;
private const int PreemptionPriorityCores012 = 59; private const int RoundRobinTimeQuantumMs = 10;
private const int PreemptionPriorityCore3 = 63;
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
private readonly KernelContext _context; private readonly KernelContext _context;
private readonly int _coreId;
public KSchedulingData SchedulingData { get; private set; } private struct SchedulingState
{
public bool NeedsScheduling;
public KThread SelectedThread;
}
public KCoreContext[] CoreContexts { get; private set; } private SchedulingState _state;
public bool ThreadReselectionRequested { get; set; } private AutoResetEvent _idleInterruptEvent;
private readonly object _idleInterruptEventLock;
public KScheduler(KernelContext context) private KThread _previousThread;
private KThread _currentThread;
private readonly KThread _idleThread;
public KThread PreviousThread => _previousThread;
public long LastContextSwitchTime { get; private set; }
public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning;
public KScheduler(KernelContext context, int coreId)
{ {
_context = context; _context = context;
_coreId = coreId;
SchedulingData = new KSchedulingData(); _idleInterruptEvent = new AutoResetEvent(false);
_idleInterruptEventLock = new object();
CoreManager = new HleCoreManager(); KThread idleThread = CreateIdleThread(context, coreId);
CoreContexts = new KCoreContext[CpuCoresCount]; _currentThread = idleThread;
_idleThread = idleThread;
idleThread.StartHostThread();
idleThread.SchedulerWaitEvent.Set();
}
private KThread CreateIdleThread(KernelContext context, int cpuCore)
{
KThread idleThread = new KThread(context);
idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop);
return idleThread;
}
public static ulong SelectThreads(KernelContext context)
{
if (context.ThreadReselectionRequested)
{
return SelectThreadsImpl(context);
}
else
{
return 0UL;
}
}
private static ulong SelectThreadsImpl(KernelContext context)
{
context.ThreadReselectionRequested = false;
ulong scheduledCoresMask = 0UL;
for (int core = 0; core < CpuCoresCount; core++) for (int core = 0; core < CpuCoresCount; core++)
{ {
CoreContexts[core] = new KCoreContext(this, CoreManager); KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
}
}
private void PreemptThreads() scheduledCoresMask |= context.Schedulers[core].SelectThread(thread);
{
_context.CriticalSection.Enter();
PreemptThread(PreemptionPriorityCores012, 0);
PreemptThread(PreemptionPriorityCores012, 1);
PreemptThread(PreemptionPriorityCores012, 2);
PreemptThread(PreemptionPriorityCore3, 3);
_context.CriticalSection.Leave();
}
private void PreemptThread(int prio, int core)
{
IEnumerable<KThread> scheduledThreads = SchedulingData.ScheduledThreads(core);
KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
// Yield priority queue.
if (selectedThread != null)
{
SchedulingData.Reschedule(prio, core, selectedThread);
}
IEnumerable<KThread> SuitableCandidates()
{
foreach (KThread thread in SchedulingData.SuggestedThreads(core))
{
int srcCore = thread.CurrentCore;
if (srcCore >= 0)
{
KThread highestPrioSrcCore = SchedulingData.ScheduledThreads(srcCore).FirstOrDefault();
if (highestPrioSrcCore != null && highestPrioSrcCore.DynamicPriority < 2)
{
break;
}
if (highestPrioSrcCore == thread)
{
continue;
}
}
// If the candidate was scheduled after the current thread, then it's not worth it.
if (selectedThread == null || selectedThread.LastScheduledTime >= thread.LastScheduledTime)
{
yield return thread;
}
}
}
// Select candidate threads that could run on this core.
// Only take into account threads that are not yet selected.
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
if (dst != null)
{
SchedulingData.TransferToCore(prio, core, dst);
selectedThread = dst;
}
// If the priority of the currently selected thread is lower than preemption priority,
// then allow threads with lower priorities to be selected aswell.
if (selectedThread != null && selectedThread.DynamicPriority > prio)
{
Func<KThread, bool> predicate = x => x.DynamicPriority >= selectedThread.DynamicPriority;
dst = SuitableCandidates().FirstOrDefault(predicate);
if (dst != null)
{
SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
}
}
ThreadReselectionRequested = true;
}
public void SelectThreads()
{
ThreadReselectionRequested = false;
for (int core = 0; core < CpuCoresCount; core++)
{
KThread thread = SchedulingData.ScheduledThreads(core).FirstOrDefault();
CoreContexts[core].SelectThread(thread);
} }
for (int core = 0; core < CpuCoresCount; core++) for (int core = 0; core < CpuCoresCount; core++)
{ {
// If the core is not idle (there's already a thread running on it), // If the core is not idle (there's already a thread running on it),
// then we don't need to attempt load balancing. // then we don't need to attempt load balancing.
if (SchedulingData.ScheduledThreads(core).Any()) if (context.PriorityQueue.ScheduledThreads(core).Any())
{ {
continue; continue;
} }
@ -146,16 +107,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// Select candidate threads that could run on this core. // Select candidate threads that could run on this core.
// Give preference to threads that are not yet selected. // Give preference to threads that are not yet selected.
foreach (KThread thread in SchedulingData.SuggestedThreads(core)) foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{ {
if (thread.CurrentCore < 0 || thread != CoreContexts[thread.CurrentCore].SelectedThread) if (suggested.ActiveCore < 0 || suggested != context.Schedulers[suggested.ActiveCore]._state.SelectedThread)
{ {
dst = thread; dst = suggested;
break; break;
} }
srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = thread.CurrentCore; srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
} }
// Not yet selected candidate found. // Not yet selected candidate found.
@ -165,9 +125,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// threads, we should skip load balancing entirely. // threads, we should skip load balancing entirely.
if (dst.DynamicPriority >= 2) if (dst.DynamicPriority >= 2)
{ {
SchedulingData.TransferToCore(dst.DynamicPriority, core, dst); context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst);
CoreContexts[core].SelectThread(dst); scheduledCoresMask |= context.Schedulers[core].SelectThread(dst);
} }
continue; continue;
@ -179,80 +139,480 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
int srcCore = srcCoresHighestPrioThreads[index]; int srcCore = srcCoresHighestPrioThreads[index];
KThread src = SchedulingData.ScheduledThreads(srcCore).ElementAtOrDefault(1); KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
if (src != null) if (src != null)
{ {
// Run the second thread on the queue on the source core, // Run the second thread on the queue on the source core,
// move the first one to the current core. // move the first one to the current core.
KThread origSelectedCoreSrc = CoreContexts[srcCore].SelectedThread; KThread origSelectedCoreSrc = context.Schedulers[srcCore]._state.SelectedThread;
CoreContexts[srcCore].SelectThread(src); scheduledCoresMask |= context.Schedulers[srcCore].SelectThread(src);
SchedulingData.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc); context.PriorityQueue.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc);
CoreContexts[core].SelectThread(origSelectedCoreSrc); scheduledCoresMask |= context.Schedulers[core].SelectThread(origSelectedCoreSrc);
}
} }
} }
} }
public KThread GetCurrentThread() return scheduledCoresMask;
}
private ulong SelectThread(KThread nextThread)
{ {
return GetCurrentThreadOrNull() ?? GetDummyThread(); KThread previousThread = _state.SelectedThread;
if (previousThread != nextThread)
{
if (previousThread != null)
{
previousThread.LastScheduledTime = PerformanceCounter.ElapsedTicks;
} }
public KThread GetCurrentThreadOrNull() _state.SelectedThread = nextThread;
_state.NeedsScheduling = true;
return 1UL << _coreId;
}
else
{ {
lock (CoreContexts) return 0UL;
}
}
public static void EnableScheduling(KernelContext context, ulong scheduledCoresMask)
{ {
KScheduler currentScheduler = context.Schedulers[KernelStatic.GetCurrentThread().CurrentCore];
// Note that "RescheduleCurrentCore" will block, so "RescheduleOtherCores" must be done first.
currentScheduler.RescheduleOtherCores(scheduledCoresMask);
currentScheduler.RescheduleCurrentCore();
}
public static void EnableSchedulingFromForeignThread(KernelContext context, ulong scheduledCoresMask)
{
RescheduleOtherCores(context, scheduledCoresMask);
}
private void RescheduleCurrentCore()
{
if (_state.NeedsScheduling)
{
Schedule();
}
}
private void RescheduleOtherCores(ulong scheduledCoresMask)
{
RescheduleOtherCores(_context, scheduledCoresMask & ~(1UL << _coreId));
}
private static void RescheduleOtherCores(KernelContext context, ulong scheduledCoresMask)
{
while (scheduledCoresMask != 0)
{
int coreToSignal = BitOperations.TrailingZeroCount(scheduledCoresMask);
KThread threadToSignal = context.Schedulers[coreToSignal]._currentThread;
// Request the thread running on that core to stop and reschedule, if we have one.
if (threadToSignal != context.Schedulers[coreToSignal]._idleThread)
{
threadToSignal.Context.RequestInterrupt();
}
// If the core is idle, ensure that the idle thread is awaken.
context.Schedulers[coreToSignal]._idleInterruptEvent.Set();
scheduledCoresMask &= ~(1UL << coreToSignal);
}
}
private void IdleThreadLoop()
{
while (_context.Running)
{
_state.NeedsScheduling = false;
Thread.MemoryBarrier();
KThread nextThread = PickNextThread(_state.SelectedThread);
if (_idleThread != nextThread)
{
_idleThread.SchedulerWaitEvent.Reset();
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, _idleThread.SchedulerWaitEvent);
}
_idleInterruptEvent.WaitOne();
}
lock (_idleInterruptEventLock)
{
_idleInterruptEvent.Dispose();
_idleInterruptEvent = null;
}
}
public void Schedule()
{
_state.NeedsScheduling = false;
Thread.MemoryBarrier();
KThread currentThread = KernelStatic.GetCurrentThread();
KThread selectedThread = _state.SelectedThread;
// If the thread is already scheduled and running on the core, we have nothing to do.
if (currentThread == selectedThread)
{
return;
}
currentThread.SchedulerWaitEvent.Reset();
currentThread.ThreadContext.Unlock();
// Wake all the threads that might be waiting until this thread context is unlocked.
for (int core = 0; core < CpuCoresCount; core++) for (int core = 0; core < CpuCoresCount; core++)
{ {
if (CoreContexts[core].CurrentThread?.IsCurrentHostThread() ?? false) _context.Schedulers[core]._idleInterruptEvent.Set();
}
KThread nextThread = PickNextThread(selectedThread);
if (currentThread.Context.Running)
{ {
return CoreContexts[core].CurrentThread; // Wait until this thread is scheduled again, and allow the next thread to run.
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent);
} }
} else
}
return null;
}
private KThread _dummyThread;
private KThread GetDummyThread()
{ {
if (_dummyThread != null) // Allow the next thread to run.
{ nextThread.SchedulerWaitEvent.Set();
return _dummyThread;
// We don't need to wait since the thread is exiting, however we need to
// make sure this thread will never call the scheduler again, since it is
// no longer assigned to a core.
currentThread.MakeUnschedulable();
// Just to be sure, set the core to a invalid value.
// This will trigger a exception if it attempts to call schedule again,
// rather than leaving the scheduler in a invalid state.
currentThread.CurrentCore = -1;
}
} }
KProcess dummyProcess = new KProcess(_context); private KThread PickNextThread(KThread selectedThread)
{
while (true)
{
if (selectedThread != null)
{
// Try to run the selected thread.
// We need to acquire the context lock to be sure the thread is not
// already running on another core. If it is, then we return here
// and the caller should try again once there is something available for scheduling.
// The thread currently running on the core should have been requested to
// interrupt so this is not expected to take long.
// The idle thread must also be paused if we are scheduling a thread
// on the core, as the scheduled thread will handle the next switch.
if (selectedThread.ThreadContext.Lock())
{
SwitchTo(selectedThread);
dummyProcess.HandleTable.Initialize(1024); if (!_state.NeedsScheduling)
{
KThread dummyThread = new KThread(_context); return selectedThread;
dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
return _dummyThread = dummyThread;
} }
public KProcess GetCurrentProcess() selectedThread.ThreadContext.Unlock();
}
else
{ {
return GetCurrentThread().Owner; return _idleThread;
}
}
else
{
// The core is idle now, make sure that the idle thread can run
// and switch the core when a thread is available.
SwitchTo(null);
return _idleThread;
}
_state.NeedsScheduling = false;
Thread.MemoryBarrier();
selectedThread = _state.SelectedThread;
}
}
private void SwitchTo(KThread nextThread)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KThread currentThread = KernelStatic.GetCurrentThread();
nextThread ??= _idleThread;
if (currentThread == nextThread)
{
return;
}
long previousTicks = LastContextSwitchTime;
long currentTicks = PerformanceCounter.ElapsedTicks;
long ticksDelta = currentTicks - previousTicks;
currentThread.AddCpuTime(ticksDelta);
if (currentProcess != null)
{
currentProcess.AddCpuTime(ticksDelta);
}
LastContextSwitchTime = currentTicks;
if (currentProcess != null)
{
_previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null;
}
else if (currentThread == _idleThread)
{
_previousThread = null;
}
if (nextThread.CurrentCore != _coreId)
{
nextThread.CurrentCore = _coreId;
}
_currentThread = nextThread;
}
public static void PreemptionThreadLoop(KernelContext context)
{
while (context.Running)
{
context.CriticalSection.Enter();
for (int core = 0; core < CpuCoresCount; core++)
{
RotateScheduledQueue(context, core, PreemptionPriorities[core]);
}
context.CriticalSection.Leave();
Thread.Sleep(RoundRobinTimeQuantumMs);
}
}
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
{
IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
KThread nextThread = null;
// Yield priority queue.
if (selectedThread != null)
{
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
}
IEnumerable<KThread> SuitableCandidates()
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore >= 0)
{
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
{
continue;
}
}
// If the candidate was scheduled after the current thread, then it's not worth it.
if (nextThread == selectedThread ||
nextThread == null ||
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
{
yield return suggested;
}
}
}
// Select candidate threads that could run on this core.
// Only take into account threads that are not yet selected.
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
if (dst != null)
{
context.PriorityQueue.TransferToCore(prio, core, dst);
}
// If the priority of the currently selected thread is lower or same as the preemption priority,
// then try to migrate a thread with lower priority.
KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
{
dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
if (dst != null)
{
context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst);
}
}
context.ThreadReselectionRequested = true;
}
public static void Yield(KernelContext context)
{
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
{
context.CriticalSection.Leave();
return;
}
KThread nextThread = context.PriorityQueue.Reschedule(currentThread.DynamicPriority, currentThread.ActiveCore, currentThread);
if (nextThread != currentThread)
{
context.ThreadReselectionRequested = true;
}
context.CriticalSection.Leave();
}
public static void YieldWithLoadBalancing(KernelContext context)
{
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
{
context.CriticalSection.Leave();
return;
}
int prio = currentThread.DynamicPriority;
int core = currentThread.ActiveCore;
// Move current thread to the end of the queue.
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
IEnumerable<KThread> SuitableCandidates()
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore >= 0)
{
KThread selectedSuggestedCore = context.Schedulers[suggestedCore]._state.SelectedThread;
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
{
continue;
}
}
// If the candidate was scheduled after the current thread, then it's not worth it,
// unless the priority is higher than the current one.
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
suggested.DynamicPriority < nextThread.DynamicPriority)
{
yield return suggested;
}
}
}
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
if (dst != null)
{
context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst);
context.ThreadReselectionRequested = true;
}
else if (currentThread != nextThread)
{
context.ThreadReselectionRequested = true;
}
context.CriticalSection.Leave();
}
public static void YieldToAnyThread(KernelContext context)
{
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
{
context.CriticalSection.Leave();
return;
}
int core = currentThread.ActiveCore;
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
if (!context.PriorityQueue.ScheduledThreads(core).Any())
{
KThread selectedThread = null;
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore < 0)
{
continue;
}
KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
if (firstCandidate == suggested)
{
continue;
}
if (firstCandidate == null || firstCandidate.DynamicPriority >= 2)
{
context.PriorityQueue.TransferToCore(suggested.DynamicPriority, core, suggested);
}
selectedThread = suggested;
break;
}
if (currentThread != selectedThread)
{
context.ThreadReselectionRequested = true;
}
}
else
{
context.ThreadReselectionRequested = true;
}
context.CriticalSection.Leave();
} }
public void Dispose() public void Dispose()
{ {
Dispose(true); // Ensure that the idle thread is not blocked and can exit.
lock (_idleInterruptEventLock)
{
if (_idleInterruptEvent != null)
{
_idleInterruptEvent.Set();
} }
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_keepPreempting = false;
} }
} }
} }

View file

@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return result; return result;
} }
KThread currentThread = _context.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
if (currentThread.ShallBeTerminated || if (currentThread.ShallBeTerminated ||
currentThread.SchedFlags == ThreadSchedState.TerminationPending) currentThread.SchedFlags == ThreadSchedState.TerminationPending)

View file

@ -4,8 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Numerics;
using System.Text;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading namespace Ryujinx.HLE.HOS.Kernel.Threading
@ -14,17 +13,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
public const int MaxWaitSyncObjects = 64; public const int MaxWaitSyncObjects = 64;
private int _hostThreadRunning; private ManualResetEvent _schedulerWaitEvent;
public ManualResetEvent SchedulerWaitEvent => _schedulerWaitEvent;
public Thread HostThread { get; private set; } public Thread HostThread { get; private set; }
public ARMeilleure.State.ExecutionContext Context { get; private set; } public ARMeilleure.State.ExecutionContext Context { get; private set; }
public KThreadContext ThreadContext { get; private set; }
public int DynamicPriority { get; set; }
public long AffinityMask { get; set; } public long AffinityMask { get; set; }
public long ThreadUid { get; private set; } public long ThreadUid { get; private set; }
public long TotalTimeRunning { get; set; } private long _totalTimeRunning;
public long TotalTimeRunning => _totalTimeRunning;
public KSynchronizationObject SignaledObj { get; set; } public KSynchronizationObject SignaledObj { get; set; }
@ -32,6 +38,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private ulong _entrypoint; private ulong _entrypoint;
private ThreadStart _customThreadStart; private ThreadStart _customThreadStart;
private bool _forcedUnschedulable;
public bool IsSchedulable => _customThreadStart == null && !_forcedUnschedulable;
public ulong MutexAddress { get; set; } public ulong MutexAddress { get; set; }
@ -65,11 +74,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult ObjSyncResult { get; set; } public KernelResult ObjSyncResult { get; set; }
public int DynamicPriority { get; set; }
public int CurrentCore { get; set; }
public int BasePriority { get; set; } public int BasePriority { get; set; }
public int PreferredCore { get; set; } public int PreferredCore { get; set; }
public int CurrentCore { get; set; }
public int ActiveCore { get; set; }
private long _affinityMaskOverride; private long _affinityMaskOverride;
private int _preferredCoreOverride; private int _preferredCoreOverride;
#pragma warning disable CS0649 #pragma warning disable CS0649
@ -86,26 +96,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
set => _shallBeTerminated = value ? 1 : 0; set => _shallBeTerminated = value ? 1 : 0;
} }
public bool TerminationRequested => ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending;
public bool SyncCancelled { get; set; } public bool SyncCancelled { get; set; }
public bool WaitingSync { get; set; } public bool WaitingSync { get; set; }
private bool _hasExited; private int _hasExited;
private bool _hasBeenInitialized; private bool _hasBeenInitialized;
private bool _hasBeenReleased; private bool _hasBeenReleased;
public bool WaitingInArbitration { get; set; } public bool WaitingInArbitration { get; set; }
private KScheduler _scheduler;
private KSchedulingData _schedulingData;
public long LastPc { get; set; } public long LastPc { get; set; }
public KThread(KernelContext context) : base(context) public KThread(KernelContext context) : base(context)
{ {
_scheduler = KernelContext.Scheduler;
_schedulingData = KernelContext.Scheduler.SchedulingData;
WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects]; WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
WaitSyncHandles = new int[MaxWaitSyncObjects]; WaitSyncHandles = new int[MaxWaitSyncObjects];
@ -119,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
ulong argsPtr, ulong argsPtr,
ulong stackTop, ulong stackTop,
int priority, int priority,
int defaultCpuCore, int cpuCore,
KProcess owner, KProcess owner,
ThreadType type, ThreadType type,
ThreadStart customThreadStart = null) ThreadStart customThreadStart = null)
@ -129,20 +134,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
throw new ArgumentException($"Invalid thread type \"{type}\"."); throw new ArgumentException($"Invalid thread type \"{type}\".");
} }
PreferredCore = defaultCpuCore; ThreadContext = new KThreadContext();
AffinityMask |= 1L << defaultCpuCore; PreferredCore = cpuCore;
AffinityMask |= 1L << cpuCore;
SchedFlags = type == ThreadType.Dummy SchedFlags = type == ThreadType.Dummy
? ThreadSchedState.Running ? ThreadSchedState.Running
: ThreadSchedState.None; : ThreadSchedState.None;
CurrentCore = PreferredCore; ActiveCore = cpuCore;
ObjSyncResult = KernelResult.ThreadNotStarted;
DynamicPriority = priority; DynamicPriority = priority;
BasePriority = priority; BasePriority = priority;
CurrentCore = cpuCore;
ObjSyncResult = KernelResult.ThreadNotStarted;
_entrypoint = entrypoint; _entrypoint = entrypoint;
_customThreadStart = customThreadStart; _customThreadStart = customThreadStart;
@ -179,41 +184,38 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Context = CpuContext.CreateExecutionContext(); Context = CpuContext.CreateExecutionContext();
bool isAarch32 = !Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit); Context.IsAarch32 = !is64Bits;
Context.IsAarch32 = isAarch32;
Context.SetX(0, argsPtr); Context.SetX(0, argsPtr);
if (isAarch32) if (is64Bits)
{ {
Context.SetX(13, (uint)stackTop); Context.SetX(31, stackTop);
} }
else else
{ {
Context.SetX(31, stackTop); Context.SetX(13, (uint)stackTop);
} }
Context.CntfrqEl0 = 19200000; Context.CntfrqEl0 = 19200000;
Context.Tpidr = (long)_tlsAddress; Context.Tpidr = (long)_tlsAddress;
owner.SubscribeThreadEventHandlers(Context);
ThreadUid = KernelContext.NewThreadUid(); ThreadUid = KernelContext.NewThreadUid();
HostThread.Name = $"HLE.HostThread.{ThreadUid}"; HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}";
_hasBeenInitialized = true; _hasBeenInitialized = true;
if (owner != null) if (owner != null)
{ {
owner.SubscribeThreadEventHandlers(Context);
owner.AddThread(this); owner.AddThread(this);
if (owner.IsPaused) if (owner.IsPaused)
{ {
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) if (TerminationRequested)
{ {
KernelContext.CriticalSection.Leave(); KernelContext.CriticalSection.Leave();
@ -237,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending) if (!TerminationRequested)
{ {
_forcePauseFlags |= ThreadSchedState.KernelInitPauseFlag; _forcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
@ -253,20 +255,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (!ShallBeTerminated) if (!ShallBeTerminated)
{ {
KThread currentThread = KernelContext.Scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
while (SchedFlags != ThreadSchedState.TerminationPending && while (SchedFlags != ThreadSchedState.TerminationPending && (currentThread == null || !currentThread.TerminationRequested))
currentThread.SchedFlags != ThreadSchedState.TerminationPending &&
!currentThread.ShallBeTerminated)
{ {
if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None) if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
{ {
result = KernelResult.InvalidState; result = KernelResult.InvalidState;
break; break;
} }
if (currentThread._forcePauseFlags == ThreadSchedState.None) if (currentThread == null || currentThread._forcePauseFlags == ThreadSchedState.None)
{ {
if (Owner != null && _forcePauseFlags != ThreadSchedState.None) if (Owner != null && _forcePauseFlags != ThreadSchedState.None)
{ {
@ -275,8 +274,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
SetNewSchedFlags(ThreadSchedState.Running); SetNewSchedFlags(ThreadSchedState.Running);
result = KernelResult.Success; StartHostThread();
result = KernelResult.Success;
break; break;
} }
else else
@ -299,28 +299,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return result; return result;
} }
public void Exit()
{
// TODO: Debug event.
if (Owner != null)
{
Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
_hasBeenReleased = true;
}
KernelContext.CriticalSection.Enter();
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
ExitImpl();
KernelContext.CriticalSection.Leave();
DecrementReferenceCount();
}
public ThreadSchedState PrepareForTermination() public ThreadSchedState PrepareForTermination()
{ {
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
@ -387,9 +365,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
do do
{ {
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) if (TerminationRequested)
{ {
KernelContext.Scheduler.ExitThread(this);
Exit(); Exit();
// As the death of the thread is handled by the CPU emulator, we differ from the official kernel and return here. // As the death of the thread is handled by the CPU emulator, we differ from the official kernel and return here.
@ -398,7 +375,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) if (TerminationRequested)
{ {
state = ThreadSchedState.TerminationPending; state = ThreadSchedState.TerminationPending;
} }
@ -416,17 +393,46 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} while (state == ThreadSchedState.TerminationPending); } while (state == ThreadSchedState.TerminationPending);
} }
private void ExitImpl() public void Exit()
{
// TODO: Debug event.
if (Owner != null)
{
Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
_hasBeenReleased = true;
}
KernelContext.CriticalSection.Enter();
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
bool decRef = ExitImpl();
Context.StopRunning();
KernelContext.CriticalSection.Leave();
if (decRef)
{
DecrementReferenceCount();
}
}
private bool ExitImpl()
{ {
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
SetNewSchedFlags(ThreadSchedState.TerminationPending); SetNewSchedFlags(ThreadSchedState.TerminationPending);
_hasExited = true; bool decRef = Interlocked.Exchange(ref _hasExited, 1) == 0;
Signal(); Signal();
KernelContext.CriticalSection.Leave(); KernelContext.CriticalSection.Leave();
return decRef;
} }
public KernelResult Sleep(long timeout) public KernelResult Sleep(long timeout)
@ -457,161 +463,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return 0; return 0;
} }
public void Yield()
{
KernelContext.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
KernelContext.CriticalSection.Leave();
KernelContext.Scheduler.ContextSwitch();
return;
}
if (DynamicPriority < KScheduler.PrioritiesCount)
{
// Move current thread to the end of the queue.
_schedulingData.Reschedule(DynamicPriority, CurrentCore, this);
}
_scheduler.ThreadReselectionRequested = true;
KernelContext.CriticalSection.Leave();
KernelContext.Scheduler.ContextSwitch();
}
public void YieldWithLoadBalancing()
{
KernelContext.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
KernelContext.CriticalSection.Leave();
KernelContext.Scheduler.ContextSwitch();
return;
}
int prio = DynamicPriority;
int core = CurrentCore;
KThread nextThreadOnCurrentQueue = null;
if (DynamicPriority < KScheduler.PrioritiesCount)
{
// Move current thread to the end of the queue.
_schedulingData.Reschedule(prio, core, this);
Func<KThread, bool> predicate = x => x.DynamicPriority == prio;
nextThreadOnCurrentQueue = _schedulingData.ScheduledThreads(core).FirstOrDefault(predicate);
}
IEnumerable<KThread> SuitableCandidates()
{
foreach (KThread thread in _schedulingData.SuggestedThreads(core))
{
int srcCore = thread.CurrentCore;
if (srcCore >= 0)
{
KThread selectedSrcCore = _scheduler.CoreContexts[srcCore].SelectedThread;
if (selectedSrcCore == thread || ((selectedSrcCore?.DynamicPriority ?? 2) < 2))
{
continue;
}
}
// If the candidate was scheduled after the current thread, then it's not worth it,
// unless the priority is higher than the current one.
if (nextThreadOnCurrentQueue.LastScheduledTime >= thread.LastScheduledTime ||
nextThreadOnCurrentQueue.DynamicPriority < thread.DynamicPriority)
{
yield return thread;
}
}
}
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
if (dst != null)
{
_schedulingData.TransferToCore(dst.DynamicPriority, core, dst);
_scheduler.ThreadReselectionRequested = true;
}
if (this != nextThreadOnCurrentQueue)
{
_scheduler.ThreadReselectionRequested = true;
}
KernelContext.CriticalSection.Leave();
KernelContext.Scheduler.ContextSwitch();
}
public void YieldAndWaitForLoadBalancing()
{
KernelContext.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
KernelContext.CriticalSection.Leave();
KernelContext.Scheduler.ContextSwitch();
return;
}
int core = CurrentCore;
_schedulingData.TransferToCore(DynamicPriority, -1, this);
KThread selectedThread = null;
if (!_schedulingData.ScheduledThreads(core).Any())
{
foreach (KThread thread in _schedulingData.SuggestedThreads(core))
{
if (thread.CurrentCore < 0)
{
continue;
}
KThread firstCandidate = _schedulingData.ScheduledThreads(thread.CurrentCore).FirstOrDefault();
if (firstCandidate == thread)
{
continue;
}
if (firstCandidate == null || firstCandidate.DynamicPriority >= 2)
{
_schedulingData.TransferToCore(thread.DynamicPriority, core, thread);
selectedThread = thread;
}
break;
}
}
if (selectedThread != this)
{
_scheduler.ThreadReselectionRequested = true;
}
KernelContext.CriticalSection.Leave();
KernelContext.Scheduler.ContextSwitch();
}
public void SetPriority(int priority) public void SetPriority(int priority)
{ {
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
@ -751,17 +602,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (oldAffinityMask != newAffinityMask) if (oldAffinityMask != newAffinityMask)
{ {
int oldCore = CurrentCore; int oldCore = ActiveCore;
if (CurrentCore >= 0 && ((AffinityMask >> CurrentCore) & 1) == 0) if (oldCore >= 0 && ((AffinityMask >> oldCore) & 1) == 0)
{ {
if (PreferredCore < 0) if (PreferredCore < 0)
{ {
CurrentCore = HighestSetCore(AffinityMask); ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask);
} }
else else
{ {
CurrentCore = PreferredCore; ActiveCore = PreferredCore;
} }
} }
@ -774,19 +625,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return KernelResult.Success; return KernelResult.Success;
} }
private static int HighestSetCore(long mask)
{
for (int core = KScheduler.CpuCoresCount - 1; core >= 0; core--)
{
if (((mask >> core) & 1) != 0)
{
return core;
}
}
return -1;
}
private void CombineForcePauseFlags() private void CombineForcePauseFlags()
{ {
ThreadSchedState oldFlags = SchedFlags; ThreadSchedState oldFlags = SchedFlags;
@ -995,92 +833,112 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return; return;
} }
if (!IsSchedulable)
{
// Ensure our thread is running and we have an event.
StartHostThread();
// If the thread is not schedulable, we want to just run or pause
// it directly as we don't care about priority or the core it is
// running on in this case.
if (SchedFlags == ThreadSchedState.Running)
{
_schedulerWaitEvent.Set();
}
else
{
_schedulerWaitEvent.Reset();
}
return;
}
if (oldFlags == ThreadSchedState.Running) if (oldFlags == ThreadSchedState.Running)
{ {
// Was running, now it's stopped. // Was running, now it's stopped.
if (CurrentCore >= 0) if (ActiveCore >= 0)
{ {
_schedulingData.Unschedule(DynamicPriority, CurrentCore, this); KernelContext.PriorityQueue.Unschedule(DynamicPriority, ActiveCore, this);
} }
for (int core = 0; core < KScheduler.CpuCoresCount; core++) for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{ {
if (core != CurrentCore && ((AffinityMask >> core) & 1) != 0) if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
{ {
_schedulingData.Unsuggest(DynamicPriority, core, this); KernelContext.PriorityQueue.Unsuggest(DynamicPriority, core, this);
} }
} }
} }
else if (SchedFlags == ThreadSchedState.Running) else if (SchedFlags == ThreadSchedState.Running)
{ {
// Was stopped, now it's running. // Was stopped, now it's running.
if (CurrentCore >= 0) if (ActiveCore >= 0)
{ {
_schedulingData.Schedule(DynamicPriority, CurrentCore, this); KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this);
} }
for (int core = 0; core < KScheduler.CpuCoresCount; core++) for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{ {
if (core != CurrentCore && ((AffinityMask >> core) & 1) != 0) if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
{ {
_schedulingData.Suggest(DynamicPriority, core, this); KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this);
} }
} }
} }
_scheduler.ThreadReselectionRequested = true; KernelContext.ThreadReselectionRequested = true;
} }
private void AdjustSchedulingForNewPriority(int oldPriority) private void AdjustSchedulingForNewPriority(int oldPriority)
{ {
if (SchedFlags != ThreadSchedState.Running) if (SchedFlags != ThreadSchedState.Running || !IsSchedulable)
{ {
return; return;
} }
// Remove thread from the old priority queues. // Remove thread from the old priority queues.
if (CurrentCore >= 0) if (ActiveCore >= 0)
{ {
_schedulingData.Unschedule(oldPriority, CurrentCore, this); KernelContext.PriorityQueue.Unschedule(oldPriority, ActiveCore, this);
} }
for (int core = 0; core < KScheduler.CpuCoresCount; core++) for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{ {
if (core != CurrentCore && ((AffinityMask >> core) & 1) != 0) if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
{ {
_schedulingData.Unsuggest(oldPriority, core, this); KernelContext.PriorityQueue.Unsuggest(oldPriority, core, this);
} }
} }
// Add thread to the new priority queues. // Add thread to the new priority queues.
KThread currentThread = _scheduler.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
if (CurrentCore >= 0) if (ActiveCore >= 0)
{ {
if (currentThread == this) if (currentThread == this)
{ {
_schedulingData.SchedulePrepend(DynamicPriority, CurrentCore, this); KernelContext.PriorityQueue.SchedulePrepend(DynamicPriority, ActiveCore, this);
} }
else else
{ {
_schedulingData.Schedule(DynamicPriority, CurrentCore, this); KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this);
} }
} }
for (int core = 0; core < KScheduler.CpuCoresCount; core++) for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{ {
if (core != CurrentCore && ((AffinityMask >> core) & 1) != 0) if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
{ {
_schedulingData.Suggest(DynamicPriority, core, this); KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this);
} }
} }
_scheduler.ThreadReselectionRequested = true; KernelContext.ThreadReselectionRequested = true;
} }
private void AdjustSchedulingForNewAffinity(long oldAffinityMask, int oldCore) private void AdjustSchedulingForNewAffinity(long oldAffinityMask, int oldCore)
{ {
if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount) if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount || !IsSchedulable)
{ {
return; return;
} }
@ -1092,11 +950,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
if (core == oldCore) if (core == oldCore)
{ {
_schedulingData.Unschedule(DynamicPriority, core, this); KernelContext.PriorityQueue.Unschedule(DynamicPriority, core, this);
} }
else else
{ {
_schedulingData.Unsuggest(DynamicPriority, core, this); KernelContext.PriorityQueue.Unsuggest(DynamicPriority, core, this);
} }
} }
} }
@ -1106,18 +964,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
if (((AffinityMask >> core) & 1) != 0) if (((AffinityMask >> core) & 1) != 0)
{ {
if (core == CurrentCore) if (core == ActiveCore)
{ {
_schedulingData.Schedule(DynamicPriority, core, this); KernelContext.PriorityQueue.Schedule(DynamicPriority, core, this);
} }
else else
{ {
_schedulingData.Suggest(DynamicPriority, core, this); KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this);
} }
} }
} }
_scheduler.ThreadReselectionRequested = true; KernelContext.ThreadReselectionRequested = true;
} }
public void SetEntryArguments(long argsPtr, int threadHandle) public void SetEntryArguments(long argsPtr, int threadHandle)
@ -1141,17 +999,32 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Logger.Info?.Print(LogClass.Cpu, $"Guest stack trace:\n{GetGuestStackTrace()}\n"); Logger.Info?.Print(LogClass.Cpu, $"Guest stack trace:\n{GetGuestStackTrace()}\n");
} }
public void Execute() public void AddCpuTime(long ticks)
{ {
if (Interlocked.CompareExchange(ref _hostThreadRunning, 1, 0) == 0) Interlocked.Add(ref _totalTimeRunning, ticks);
}
public void StartHostThread()
{
if (_schedulerWaitEvent == null)
{
var schedulerWaitEvent = new ManualResetEvent(false);
if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null)
{ {
HostThread.Start(); HostThread.Start();
} }
else
{
schedulerWaitEvent.Dispose();
}
}
} }
private void ThreadStart() private void ThreadStart()
{ {
KernelStatic.SetKernelContext(KernelContext); _schedulerWaitEvent.WaitOne();
KernelStatic.SetKernelContext(KernelContext, this);
if (_customThreadStart != null) if (_customThreadStart != null)
{ {
@ -1162,20 +1035,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Owner.Context.Execute(Context, _entrypoint); Owner.Context.Execute(Context, _entrypoint);
} }
KernelContext.Scheduler.ExitThread(this);
KernelContext.Scheduler.RemoveThread(this);
Context.Dispose(); Context.Dispose();
_schedulerWaitEvent.Dispose();
} }
public bool IsCurrentHostThread() public void MakeUnschedulable()
{ {
return Thread.CurrentThread == HostThread; _forcedUnschedulable = true;
} }
public override bool IsSignaled() public override bool IsSignaled()
{ {
return _hasExited; return _hasExited != 0;
} }
protected override void Destroy() protected override void Destroy()

View file

@ -0,0 +1,19 @@
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KThreadContext
{
private int _locked;
public bool Lock()
{
return Interlocked.Exchange(ref _locked, 1) == 0;
}
public void Unlock()
{
Interlocked.Exchange(ref _locked, 0);
}
}
}

View file

@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services
}; };
private readonly KernelContext _context; private readonly KernelContext _context;
private readonly KProcess _selfProcess; private KProcess _selfProcess;
private readonly List<int> _sessionHandles = new List<int>(); private readonly List<int> _sessionHandles = new List<int>();
private readonly List<int> _portHandles = new List<int>(); private readonly List<int> _portHandles = new List<int>();
@ -55,11 +55,7 @@ namespace Ryujinx.HLE.HOS.Services
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
context.Syscall.CreateProcess(creationInfo, DefaultCapabilities, out int handle, null, ServerLoop); KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
_selfProcess = context.Scheduler.GetCurrentProcess().HandleTable.GetKProcess(handle);
context.Syscall.StartProcess(handle, 44, 3, 0x1000);
} }
private void AddPort(int serverPortHandle, IpcService obj) private void AddPort(int serverPortHandle, IpcService obj)
@ -82,6 +78,8 @@ namespace Ryujinx.HLE.HOS.Services
private void ServerLoop() private void ServerLoop()
{ {
_selfProcess = KernelStatic.GetCurrentProcess();
if (SmObject != null) if (SmObject != null)
{ {
_context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle); _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
@ -95,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services
InitDone.Dispose(); InitDone.Dispose();
} }
KThread thread = _context.Scheduler.GetCurrentThread(); KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress; ulong messagePtr = thread.TlsAddress;
_context.Syscall.SetHeapSize(0x200000, out ulong heapAddr); _context.Syscall.SetHeapSize(0x200000, out ulong heapAddr);
@ -107,18 +105,14 @@ namespace Ryujinx.HLE.HOS.Services
while (true) while (true)
{ {
int[] handles = _portHandles.ToArray(); int[] portHandles = _portHandles.ToArray();
int[] sessionHandles = _sessionHandles.ToArray();
int[] handles = new int[portHandles.Length + sessionHandles.Length];
for (int i = 0; i < handles.Length; i++) portHandles.CopyTo(handles, 0);
{ sessionHandles.CopyTo(handles, portHandles.Length);
if (_context.Syscall.AcceptSession(handles[i], out int serverSessionHandle) == KernelResult.Success)
{
AddSessionObj(serverSessionHandle, _ports[handles[i]]);
}
}
handles = _sessionHandles.ToArray();
// We still need a timeout here to allow the service to pick up and listen new sessions...
var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex); var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex);
thread.HandlePostSyscall(); thread.HandlePostSyscall();
@ -130,8 +124,9 @@ namespace Ryujinx.HLE.HOS.Services
replyTargetHandle = 0; replyTargetHandle = 0;
if (rc == KernelResult.Success && signaledIndex != -1) if (rc == KernelResult.Success && signaledIndex >= portHandles.Length)
{ {
// We got a IPC request, process it, pass to the appropriate service if needed.
int signaledHandle = handles[signaledIndex]; int signaledHandle = handles[signaledIndex];
if (Process(signaledHandle, heapAddr)) if (Process(signaledHandle, heapAddr))
@ -141,6 +136,15 @@ namespace Ryujinx.HLE.HOS.Services
} }
else else
{ {
if (rc == KernelResult.Success)
{
// We got a new connection, accept the session to allow servicing future requests.
if (_context.Syscall.AcceptSession(handles[signaledIndex], out int serverSessionHandle) == KernelResult.Success)
{
AddSessionObj(serverSessionHandle, _ports[handles[signaledIndex]]);
}
}
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10); _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
@ -150,8 +154,8 @@ namespace Ryujinx.HLE.HOS.Services
private bool Process(int serverSessionHandle, ulong recvListAddr) private bool Process(int serverSessionHandle, ulong recvListAddr)
{ {
KProcess process = _context.Scheduler.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = _context.Scheduler.GetCurrentThread(); KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress; ulong messagePtr = thread.TlsAddress;
ulong messageSize = 0x100; ulong messageSize = 0x100;

View file

@ -184,11 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public void WaitDequeueEvent() public void WaitDequeueEvent()
{ {
Monitor.Exit(Lock); WaitForLock();
KernelStatic.YieldUntilCompletion(WaitForLock);
Monitor.Enter(Lock);
} }
public void SignalIsAllocatingEvent() public void SignalIsAllocatingEvent()
@ -198,23 +194,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public void WaitIsAllocatingEvent() public void WaitIsAllocatingEvent()
{ {
Monitor.Exit(Lock); WaitForLock();
KernelStatic.YieldUntilCompletion(WaitForLock);
Monitor.Enter(Lock);
} }
private void WaitForLock() private void WaitForLock()
{
lock (Lock)
{ {
if (Active) if (Active)
{ {
Monitor.Wait(Lock); Monitor.Wait(Lock);
} }
} }
}
public void FreeBufferLocked(int slot) public void FreeBufferLocked(int slot)
{ {

View file

@ -115,11 +115,6 @@ namespace Ryujinx.HLE
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
{
System.EnableMultiCoreScheduling();
}
System.EnablePtc = ConfigurationState.Instance.System.EnablePtc; System.EnablePtc = ConfigurationState.Instance.System.EnablePtc;
System.FsIntegrityCheckLevel = GetIntegrityCheckLevel(); System.FsIntegrityCheckLevel = GetIntegrityCheckLevel();

View file

@ -22,7 +22,6 @@
"check_updates_on_start": true, "check_updates_on_start": true,
"enable_vsync": true, "enable_vsync": true,
"enable_shader_cache": true, "enable_shader_cache": true,
"enable_multicore_scheduling": true,
"enable_ptc": false, "enable_ptc": false,
"enable_fs_integrity_checks": true, "enable_fs_integrity_checks": true,
"fs_global_access_log_mode": 0, "fs_global_access_log_mode": 0,

View file

@ -201,3 +201,28 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
``` ```
# Atmosphère (MIT)
```
MIT License
Copyright (c) 2018-2020 Atmosphère-NX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View file

@ -43,7 +43,6 @@ namespace Ryujinx.Ui
[GUI] CheckButton _checkUpdatesToggle; [GUI] CheckButton _checkUpdatesToggle;
[GUI] CheckButton _vSyncToggle; [GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _shaderCacheToggle; [GUI] CheckButton _shaderCacheToggle;
[GUI] CheckButton _multiSchedToggle;
[GUI] CheckButton _ptcToggle; [GUI] CheckButton _ptcToggle;
[GUI] CheckButton _fsicToggle; [GUI] CheckButton _fsicToggle;
[GUI] CheckButton _ignoreToggle; [GUI] CheckButton _ignoreToggle;
@ -188,11 +187,6 @@ namespace Ryujinx.Ui
_shaderCacheToggle.Click(); _shaderCacheToggle.Click();
} }
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
{
_multiSchedToggle.Click();
}
if (ConfigurationState.Instance.System.EnablePtc) if (ConfigurationState.Instance.System.EnablePtc)
{ {
_ptcToggle.Click(); _ptcToggle.Click();
@ -401,7 +395,6 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active;
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active;
ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active;
ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;

View file

@ -1439,24 +1439,6 @@
<property name="position">4</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkCheckButton" id="_multiSchedToggle">
<property name="label" translatable="yes">Enable Multicore Scheduling</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="tooltip-text" translatable="yes">Enables or disables multi-core scheduling of threads</property>
<property name="halign">start</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child> <child>
<object class="GtkCheckButton" id="_ptcToggle"> <object class="GtkCheckButton" id="_ptcToggle">
<property name="label" translatable="yes">Enable Profiled Persistent Translation Cache</property> <property name="label" translatable="yes">Enable Profiled Persistent Translation Cache</property>

View file

@ -18,7 +18,6 @@
"system_region", "system_region",
"docked_mode", "docked_mode",
"enable_vsync", "enable_vsync",
"enable_multicore_scheduling",
"enable_ptc", "enable_ptc",
"enable_fs_integrity_checks", "enable_fs_integrity_checks",
"fs_global_access_log_mode", "fs_global_access_log_mode",
@ -1196,17 +1195,6 @@
false false
] ]
}, },
"enable_multicore_scheduling": {
"$id": "#/properties/enable_multicore_scheduling",
"type": "boolean",
"title": "Enable Multicore Scheduling",
"description": "Enables or disables multi-core scheduling of threads",
"default": true,
"examples": [
true,
false
]
},
"enable_ptc": { "enable_ptc": {
"$id": "#/properties/enable_ptc", "$id": "#/properties/enable_ptc",
"type": "boolean", "type": "boolean",