aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-12-09 19:20:05 -0300
committerGitHub <noreply@github.com>2020-12-09 19:20:05 -0300
commit48278905d1470f89be31668c738397f569af156a (patch)
tree2e35b0695b33c8eb723f5948e3f6f040d84cfe76
parent3484265d37732b32951709e5abfa52a260db349d (diff)
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
-rw-r--r--Ryujinx.Common/Configuration/ConfigurationState.cs31
-rw-r--r--Ryujinx.Common/PerformanceCounter.cs4
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs23
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs15
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs87
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs4
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs3
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelContext.cs39
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelStatic.cs57
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs4
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs38
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs173
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/HleCoreManager.cs66
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs150
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs20
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs79
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs70
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs (renamed from Ryujinx.HLE/HOS/Kernel/Threading/KSchedulingData.cs)46
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs606
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs413
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs44
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs19
-rw-r--r--Ryujinx.HLE/Switch.cs5
-rw-r--r--Ryujinx/Config.json3
-rw-r--r--Ryujinx/THIRDPARTY.md25
-rw-r--r--Ryujinx/Ui/SettingsWindow.cs9
-rw-r--r--Ryujinx/Ui/SettingsWindow.glade18
-rw-r--r--Ryujinx/_schema.json12
37 files changed, 1023 insertions, 1103 deletions
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index ef4a4337..94c9ac0e 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -25,7 +25,7 @@ namespace Ryujinx.Configuration
public ReactiveObject<bool> DevColumn { get; private set; }
public ReactiveObject<bool> VersionColumn { get; private set; }
public ReactiveObject<bool> TimePlayedColumn { get; private set; }
- public ReactiveObject<bool> LastPlayedColumn { get; private set; }
+ public ReactiveObject<bool> LastPlayedColumn { get; private set; }
public ReactiveObject<bool> FileExtColumn { get; private set; }
public ReactiveObject<bool> FileSizeColumn { get; private set; }
public ReactiveObject<bool> PathColumn { get; private set; }
@@ -199,11 +199,6 @@ namespace Ryujinx.Configuration
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>
/// Enables or disables profiled translation cache persistency
/// </summary>
public ReactiveObject<bool> EnablePtc { get; private set; }
@@ -230,17 +225,16 @@ namespace Ryujinx.Configuration
public SystemSection()
{
- Language = new ReactiveObject<Language>();
- Region = new ReactiveObject<Region>();
- TimeZone = new ReactiveObject<string>();
- SystemTimeOffset = new ReactiveObject<long>();
- EnableDockedMode = new ReactiveObject<bool>();
- EnableMulticoreScheduling = new ReactiveObject<bool>();
- EnablePtc = new ReactiveObject<bool>();
- EnableFsIntegrityChecks = new ReactiveObject<bool>();
- FsGlobalAccessLogMode = new ReactiveObject<int>();
- AudioBackend = new ReactiveObject<AudioBackend>();
- IgnoreMissingServices = new ReactiveObject<bool>();
+ Language = new ReactiveObject<Language>();
+ Region = new ReactiveObject<Region>();
+ TimeZone = new ReactiveObject<string>();
+ SystemTimeOffset = new ReactiveObject<long>();
+ EnableDockedMode = new ReactiveObject<bool>();
+ EnablePtc = new ReactiveObject<bool>();
+ EnableFsIntegrityChecks = new ReactiveObject<bool>();
+ FsGlobalAccessLogMode = new ReactiveObject<int>();
+ AudioBackend = new ReactiveObject<AudioBackend>();
+ IgnoreMissingServices = new ReactiveObject<bool>();
}
}
@@ -414,7 +408,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart = CheckUpdatesOnStart,
EnableVsync = Graphics.EnableVsync,
EnableShaderCache = Graphics.EnableShaderCache,
- EnableMulticoreScheduling = System.EnableMulticoreScheduling,
EnablePtc = System.EnablePtc,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
@@ -476,7 +469,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart.Value = true;
Graphics.EnableVsync.Value = true;
Graphics.EnableShaderCache.Value = true;
- System.EnableMulticoreScheduling.Value = true;
System.EnablePtc.Value = false;
System.EnableFsIntegrityChecks.Value = true;
System.FsGlobalAccessLogMode.Value = 0;
@@ -788,7 +780,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
- System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
diff --git a/Ryujinx.Common/PerformanceCounter.cs b/Ryujinx.Common/PerformanceCounter.cs
index 1c2782e3..97ee23a2 100644
--- a/Ryujinx.Common/PerformanceCounter.cs
+++ b/Ryujinx.Common/PerformanceCounter.cs
@@ -32,7 +32,7 @@ namespace Ryujinx.Common
public static long TicksPerMillisecond { get; }
/// <summary>
- /// Gets the number of milliseconds elapsed since the system started.
+ /// Gets the number of ticks elapsed since the system started.
/// </summary>
public static long ElapsedTicks
{
@@ -76,7 +76,7 @@ namespace Ryujinx.Common
TicksPerHour = TicksPerMinute * 60;
TicksPerDay = TicksPerHour * 24;
- _ticksToNs = 1000000000.0 / (double)Stopwatch.Frequency;
+ _ticksToNs = 1000000000.0 / Stopwatch.Frequency;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 6902c899..16b3b807 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -82,9 +82,6 @@ namespace Ryujinx.HLE.HOS
public Keyset KeySet => Device.FileSystem.KeySet;
-#pragma warning disable CS0649
- private bool _hasStarted;
-#pragma warning restore CS0649
private bool _isDisposed;
public bool EnablePtc { get; set; }
@@ -300,22 +297,6 @@ namespace Ryujinx.HLE.HOS
VsyncEvent.ReadableEvent.Signal();
}
- public void EnableMultiCoreScheduling()
- {
- if (!_hasStarted)
- {
- KernelContext.Scheduler.MultiCoreScheduling = true;
- }
- }
-
- public void DisableMultiCoreScheduling()
- {
- if (!_hasStarted)
- {
- KernelContext.Scheduler.MultiCoreScheduling = false;
- }
- }
-
public void Dispose()
{
Dispose(true);
@@ -346,9 +327,7 @@ namespace Ryujinx.HLE.HOS
}
// Exit ourself now!
- KernelContext.Scheduler.ExitThread(terminationThread);
- KernelContext.Scheduler.GetCurrentThread().Exit();
- KernelContext.Scheduler.RemoveThread(terminationThread);
+ KernelStatic.GetCurrentThread().Exit();
});
terminationThread.Start();
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs b/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
index 812cbd30..a94b280f 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Common
@@ -47,17 +48,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public void IncrementReferenceCount()
{
- Interlocked.Increment(ref _referenceCount);
+ int newRefCount = Interlocked.Increment(ref _referenceCount);
+
+ Debug.Assert(newRefCount >= 2);
}
public void DecrementReferenceCount()
{
- if (Interlocked.Decrement(ref _referenceCount) == 0)
+ int newRefCount = Interlocked.Decrement(ref _referenceCount);
+
+ Debug.Assert(newRefCount >= 0);
+
+ if (newRefCount == 0)
{
Destroy();
}
}
- protected virtual void Destroy() { }
+ protected virtual void Destroy()
+ {
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
index 8273520f..f7a1c24a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -10,9 +10,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
private class WaitingObject
{
- public IKFutureSchedulerObject Object { get; private set; }
-
- public long TimePoint { get; private set; }
+ public IKFutureSchedulerObject Object { get; }
+ public long TimePoint { get; }
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 bool _keepRunning;
- public KTimeManager()
+ public KTimeManager(KernelContext context)
{
+ _context = context;
_waitingObjects = new List<WaitingObject>();
-
_keepRunning = true;
Thread work = new Thread(WaitAndCheckScheduledObjects)
@@ -45,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
long timePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(timeout);
- lock (_waitingObjects)
+ lock (_context.CriticalSection.Lock)
{
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
}
@@ -53,33 +51,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_waitEvent.Set();
}
- public static long ConvertNanosecondsToMilliseconds(long time)
- {
- time /= 1000000;
-
- if ((ulong)time > int.MaxValue)
- {
- return int.MaxValue;
- }
-
- return time;
- }
-
- public static long ConvertMillisecondsToNanoseconds(long time)
- {
- return time * 1000000;
- }
-
- public static long ConvertMillisecondsToTicks(long time)
- {
- return time * 19200;
- }
-
- public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
+ public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj)
{
- lock (_waitingObjects)
+ lock (_context.CriticalSection.Lock)
{
- _waitingObjects.RemoveAll(x => x.Object == Object);
+ _waitingObjects.RemoveAll(x => x.Object == schedulerObj);
}
}
@@ -91,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
WaitingObject next;
- lock (_waitingObjects)
+ lock (_context.CriticalSection.Lock)
{
next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
}
@@ -109,16 +85,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
if (timeUp)
{
- lock (_waitingObjects)
+ lock (_context.CriticalSection.Lock)
{
- timeUp = _waitingObjects.Remove(next);
+ if (_waitingObjects.Remove(next))
+ {
+ next.Object.TimeUp();
+ }
}
}
-
- if (timeUp)
- {
- next.Object.TimeUp();
- }
}
else
{
@@ -128,19 +102,32 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
}
}
- public void Dispose()
+ public static long ConvertNanosecondsToMilliseconds(long time)
+ {
+ time /= 1000000;
+
+ if ((ulong)time > int.MaxValue)
+ {
+ return int.MaxValue;
+ }
+
+ return time;
+ }
+
+ public static long ConvertMillisecondsToNanoseconds(long time)
{
- Dispose(true);
+ return time * 1000000;
}
- protected virtual void Dispose(bool disposing)
+ public static long ConvertHostTicksToTicks(long time)
{
- if (disposing)
- {
- _keepRunning = false;
+ return (long)((time / (double)PerformanceCounter.TicksPerSecond) * 19200000.0);
+ }
- _waitEvent?.Set();
- }
+ public void Dispose()
+ {
+ _keepRunning = false;
+ _waitEvent?.Set();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
index d57ca481..3002b6a9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
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) &&
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)
{
- KProcess currentProcess = context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
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)
{
- KProcess currentProcess = context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) &&
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)
{
- KProcess currentProcess = context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) &&
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)
{
- KProcess currentProcess = context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.CpuMemory.IsMapped(address) &&
currentProcess.CpuMemory.IsMapped(address + 7))
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
index c3b7d1dd..3f34793f 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
@@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
clientSession = null;
- KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
@@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
clientSession = null;
- KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
index d535bf82..a243a645 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
@@ -25,13 +25,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
State = ChannelState.Open;
- CreatorProcess = context.Scheduler.GetCurrentProcess();
+ CreatorProcess = KernelStatic.GetCurrentProcess();
CreatorProcess.IncrementReferenceCount();
}
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);
@@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
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);
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
index e9c6127f..e97309d9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
@@ -214,7 +214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
- KThread serverThread = KernelContext.Scheduler.GetCurrentThread();
+ KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner;
KernelContext.CriticalSection.Enter();
@@ -594,7 +594,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
- KThread serverThread = KernelContext.Scheduler.GetCurrentThread();
+ KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner;
KernelContext.CriticalSection.Enter();
@@ -889,7 +889,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private MessageHeader GetServerMessageHeader(Message serverMsg)
{
- KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
uint word0 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 0);
uint word1 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 4);
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
index d614857c..13cf4b51 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
@@ -1,6 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
-using System;
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)
{
+ IncrementReferenceCount();
+
ServerSession = new KServerSession(context, this);
ClientSession = new KClientSession(context, this, parentPort);
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
index d19c43ee..cacb7fb3 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
@@ -19,6 +19,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool KernelInitialized { get; }
+ public bool Running { get; private set; }
+
public Switch Device { get; }
public MemoryBlock Memory { get; }
public Syscall Syscall { get; }
@@ -34,7 +36,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public KSlabHeap UserSlabHeapPages { get; }
public KCriticalSection CriticalSection { get; }
- public KScheduler Scheduler { get; }
+ public KScheduler[] Schedulers { get; }
+ public KPriorityQueue PriorityQueue { get; }
public KTimeManager TimeManager { get; }
public KSynchronization Synchronization { get; }
public KContextIdManager ContextIdManager { get; }
@@ -42,6 +45,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public ConcurrentDictionary<long, KProcess> Processes { get; }
public ConcurrentDictionary<string, KAutoObject> AutoObjectNames { get; }
+ public bool ThreadReselectionRequested { get; set; }
+
private long _kipId;
private long _processId;
private long _threadUid;
@@ -51,6 +56,8 @@ namespace Ryujinx.HLE.HOS.Kernel
Device = device;
Memory = memory;
+ Running = true;
+
Syscall = new Syscall(this);
SyscallHandler = new SyscallHandler(this);
@@ -70,12 +77,18 @@ namespace Ryujinx.HLE.HOS.Kernel
KernelConstants.UserSlabHeapSize);
CriticalSection = new KCriticalSection(this);
- Scheduler = new KScheduler(this);
- TimeManager = new KTimeManager();
+ Schedulers = new KScheduler[KScheduler.CpuCoresCount];
+ PriorityQueue = new KPriorityQueue();
+ TimeManager = new KTimeManager(this);
Synchronization = new KSynchronization(this);
ContextIdManager = new KContextIdManager();
- Scheduler.StartAutoPreemptionThread();
+ for (int core = 0; core < KScheduler.CpuCoresCount; core++)
+ {
+ Schedulers[core] = new KScheduler(this, core);
+ }
+
+ StartPreemptionThread();
KernelInitialized = true;
@@ -86,6 +99,16 @@ namespace Ryujinx.HLE.HOS.Kernel
_processId = KernelConstants.InitialProcessId;
}
+ private void StartPreemptionThread()
+ {
+ void PreemptionThreadStart()
+ {
+ KScheduler.PreemptionThreadLoop(this);
+ }
+
+ new Thread(PreemptionThreadStart) { Name = "HLE.PreemptionThread" }.Start();
+ }
+
public long NewThreadUid()
{
return Interlocked.Increment(ref _threadUid) - 1;
@@ -103,7 +126,13 @@ namespace Ryujinx.HLE.HOS.Kernel
public void Dispose()
{
- Scheduler.Dispose();
+ Running = false;
+
+ for (int i = 0; i < KScheduler.CpuCoresCount; i++)
+ {
+ Schedulers[i].Dispose();
+ }
+
TimeManager.Dispose();
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs
index c7deadae..2446ace4 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs
@@ -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.Threading.Tasks;
+using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
@@ -9,30 +12,52 @@ namespace Ryujinx.HLE.HOS.Kernel
[ThreadStatic]
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);
- public static void YieldUntilCompletion(Task task)
- {
- KThread currentThread = Context.Scheduler.GetCurrentThread();
+ KernelResult result = process.Initialize(
+ creationInfo,
+ capabilities,
+ context.ResourceLimit,
+ MemoryRegion.Service,
+ null,
+ customThreadStart);
- Context.CriticalSection.Enter();
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
- currentThread.Reschedule(ThreadSchedState.Paused);
+ process.DefaultCpuCore = 3;
- task.ContinueWith((antecedent) =>
- {
- currentThread.Reschedule(ThreadSchedState.Running);
- });
+ context.Processes.TryAdd(process.Pid, process);
- Context.CriticalSection.Leave();
+ return process.Start(mainThreadPriority, 0x1000UL);
}
- internal static void SetKernelContext(KernelContext context)
+ internal static void SetKernelContext(KernelContext context, KThread thread)
{
Context = context;
+ CurrentThread = thread;
+ }
+
+ internal static KThread GetCurrentThread()
+ {
+ return CurrentThread;
+ }
+
+ internal static KProcess GetCurrentProcess()
+ {
+ return GetCurrentThread().Owner;
}
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
index 05ad9f61..5a8e8e3b 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
@@ -728,7 +728,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfMemory;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
lock (_blocks)
{
@@ -1225,7 +1225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong remainingPages = remainingSize / PageSize;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Memory, remainingSize))
@@ -1355,7 +1355,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
PhysicalMemoryUsage -= heapMappedSize;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
currentProcess.ResourceLimit?.Release(LimitableResource.Memory, heapMappedSize);
@@ -1504,7 +1504,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
attributeMask | MemoryAttribute.Uncached,
attributeExpected))
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
serverAddress = currentProcess.MemoryManager.GetDramAddressFromVa(serverAddress);
@@ -2111,11 +2111,11 @@ 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)
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
index 96349452..7107d497 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
@@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission)
{
- KProcess creator = KernelContext.Scheduler.GetCurrentProcess();
+ KProcess creator = KernelStatic.GetCurrentProcess();
_creator = creator;
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
index b733501a..bcbb3b03 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
@@ -236,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
if (handle == SelfThreadHandle)
{
- return _context.Scheduler.GetCurrentThread();
+ return KernelStatic.GetCurrentThread();
}
else
{
@@ -248,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
if (handle == SelfProcessHandle)
{
- return _context.Scheduler.GetCurrentProcess();
+ return KernelStatic.GetCurrentProcess();
}
else
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index 8e914f19..d1990169 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -78,6 +78,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public bool IsPaused { get; private set; }
+ private long _totalTimeRunning;
+
+ public long TotalTimeRunning => _totalTimeRunning;
+
private IProcessContextFactory _contextFactory;
public IProcessContext Context { get; private set; }
public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
@@ -112,11 +116,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
KPageList pageList,
KResourceLimit resourceLimit,
MemoryRegion memRegion,
- IProcessContextFactory contextFactory)
+ IProcessContextFactory contextFactory,
+ ThreadStart customThreadStart = null)
{
ResourceLimit = resourceLimit;
_memRegion = memRegion;
_contextFactory = contextFactory ?? new ProcessContextFactory();
+ _customThreadStart = customThreadStart;
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}.");
}
- result = ParseProcessInfo(creationInfo);
-
- return result;
+ return ParseProcessInfo(creationInfo);
}
public KernelResult Initialize(
@@ -192,6 +196,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ResourceLimit = resourceLimit;
_memRegion = memRegion;
_contextFactory = contextFactory ?? new ProcessContextFactory();
+ _customThreadStart = customThreadStart;
ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion);
@@ -299,8 +304,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
CleanUpForError();
}
- _customThreadStart = customThreadStart;
-
return result;
}
@@ -751,8 +754,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private void InterruptHandler(object sender, EventArgs e)
{
- KernelContext.Scheduler.ContextSwitch();
- KernelContext.Scheduler.GetCurrentThread().HandlePostSyscall();
+ KThread currentThread = KernelStatic.GetCurrentThread();
+
+ if (currentThread.IsSchedulable)
+ {
+ KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
+ }
+
+ currentThread.HandlePostSyscall();
}
public void IncrementThreadCount()
@@ -828,6 +837,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return personalMmHeapPagesCount * KMemoryManager.PageSize;
}
+ public void AddCpuTime(long ticks)
+ {
+ Interlocked.Add(ref _totalTimeRunning, ticks);
+ }
+
public void AddThread(KThread thread)
{
lock (_threadingLock)
@@ -893,7 +907,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
if (shallTerminate)
{
- UnpauseAndTerminateAllThreadsExcept(KernelContext.Scheduler.GetCurrentThread());
+ UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
HandleTable.Destroy();
@@ -929,7 +943,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
if (shallTerminate)
{
- UnpauseAndTerminateAllThreadsExcept(KernelContext.Scheduler.GetCurrentThread());
+ UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
HandleTable.Destroy();
@@ -1058,7 +1072,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
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}.");
@@ -1067,7 +1081,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
{
- KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace();
+ KernelStatic.GetCurrentThread().PrintGuestStackTrace();
throw new UndefinedInstructionException(e.Address, e.OpCode);
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index 5e32ca58..418a02f0 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetProcessId(int handle, out long pid)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidThread;
}
- KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable;
+ KHandleTable handleTable = KernelStatic.GetCurrentProcess().HandleTable;
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)
{
- KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
+ KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
if (process == null)
{
@@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.NotFound;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle);
@@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SendSyncRequest(int handle)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
@@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
@@ -303,7 +303,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
@@ -363,7 +363,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
serverSessionHandle = 0;
clientSessionHandle = 0;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KResourceLimit resourceLimit = currentProcess.ResourceLimit;
@@ -424,7 +424,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
sessionHandle = 0;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle);
@@ -485,7 +485,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)((long)handlesCount * 4);
@@ -513,7 +513,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
handleIndex = 0;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
@@ -582,7 +582,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
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);
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
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);
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
@@ -756,7 +756,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
clientSessionHandle = 0;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle);
@@ -814,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.SetHeapSize(size, out position);
}
@@ -843,7 +843,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.MemoryManager.SetMemoryAttribute(
position,
@@ -871,7 +871,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{
@@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.Map(dst, src, size);
}
@@ -907,7 +907,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{
@@ -921,14 +921,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.Unmap(dst, src, size);
}
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);
@@ -968,7 +968,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
@@ -1009,7 +1009,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
@@ -1056,7 +1056,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KResourceLimit resourceLimit = process.ResourceLimit;
@@ -1112,7 +1112,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
@@ -1125,7 +1125,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.MapPhysicalMemory(address, size);
}
@@ -1147,7 +1147,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
@@ -1160,7 +1160,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.UnmapPhysicalMemory(address, size);
}
@@ -1177,7 +1177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@@ -1214,7 +1214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@@ -1259,7 +1259,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@@ -1285,7 +1285,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult TerminateProcess(int handle)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
process = process.HandleTable.GetObject<KProcess>(handle);
@@ -1293,7 +1293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (process != null)
{
- if (process == _context.Scheduler.GetCurrentProcess())
+ if (process == KernelStatic.GetCurrentProcess())
{
result = KernelResult.Success;
process.DecrementToZeroWhileTerminatingCurrent();
@@ -1314,12 +1314,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void ExitProcess()
{
- _context.Scheduler.GetCurrentProcess().TerminateCurrentProcess();
+ KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
}
public KernelResult SignalEvent(int handle)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle);
@@ -1343,7 +1343,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
KernelResult result;
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle);
@@ -1363,14 +1363,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult CloseHandle(int handle)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
}
public KernelResult ResetSignal(int handle)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
@@ -1399,12 +1399,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public ulong GetSystemTick()
{
- return _context.Scheduler.GetCurrentThread().Context.CntpctEl0;
+ return KernelStatic.GetCurrentThread().Context.CntpctEl0;
}
public void Break(ulong reason)
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
if ((reason & (1UL << 31)) == 0)
{
@@ -1429,7 +1429,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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);
@@ -1466,7 +1466,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
@@ -1537,7 +1537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
- value = _context.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
+ value = KernelStatic.GetCurrentProcess().Debug ? 1 : 0;
break;
}
@@ -1554,7 +1554,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null)
{
@@ -1581,14 +1581,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
- int currentCore = _context.Scheduler.GetCurrentThread().CurrentCore;
+ int currentCore = KernelStatic.GetCurrentThread().CurrentCore;
if (subId != -1 && subId != currentCore)
{
return KernelResult.InvalidCombination;
}
- value = _context.Scheduler.CoreContexts[currentCore].TotalIdleTimeTicks;
+ value = KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks);
break;
}
@@ -1605,8 +1605,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
-
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
value = currentProcess.RandomEntropy[subId];
@@ -1620,14 +1619,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
- KThread thread = _context.Scheduler.GetCurrentProcess().HandleTable.GetKThread(handle);
+ KThread thread = KernelStatic.GetCurrentProcess().HandleTable.GetKThread(handle);
if (thread == null)
{
return KernelResult.InvalidHandle;
}
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
int currentCore = currentThread.CurrentCore;
@@ -1636,13 +1635,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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)
{
- value = KTimeManager.ConvertMillisecondsToTicks(timeDelta);
+ value = KTimeManager.ConvertHostTicksToTicks(timeDelta);
}
else
{
@@ -1653,7 +1652,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
totalTimeRunning += timeDelta;
}
- value = KTimeManager.ConvertMillisecondsToTicks(totalTimeRunning);
+ value = KTimeManager.ConvertHostTicksToTicks(totalTimeRunning);
}
break;
@@ -1669,7 +1668,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
KEvent Event = new KEvent(_context);
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle);
@@ -1701,7 +1700,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (maxCount != 0)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)maxCount * 8;
@@ -1807,7 +1806,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
handle = 0;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (cpuCore == -2)
{
@@ -1844,7 +1843,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (result == KernelResult.Success)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
result = process.HandleTable.GenerateHandle(thread, out handle);
}
@@ -1860,7 +1859,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult StartThread(int handle)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@@ -1887,35 +1886,31 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void ExitThread()
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
-
- _context.Scheduler.ExitThread(currentThread);
+ KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.Exit();
}
public void SleepThread(long timeout)
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
-
if (timeout < 1)
{
switch (timeout)
{
- case 0: currentThread.Yield(); break;
- case -1: currentThread.YieldWithLoadBalancing(); break;
- case -2: currentThread.YieldAndWaitForLoadBalancing(); break;
+ case 0: KScheduler.Yield(_context); break;
+ case -1: KScheduler.YieldWithLoadBalancing(_context); break;
+ case -2: KScheduler.YieldToAnyThread(_context); break;
}
}
else
{
- currentThread.Sleep(timeout);
+ KernelStatic.GetCurrentThread().Sleep(timeout);
}
}
public KernelResult GetThreadPriority(int handle, out int priority)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@@ -1937,7 +1932,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
// TODO: NPDM check.
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
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)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
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)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
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);
@@ -2023,12 +2018,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public int GetCurrentProcessorNumber()
{
- return _context.Scheduler.GetCurrentThread().CurrentCore;
+ return KernelStatic.GetCurrentThread().CurrentCore;
}
public KernelResult GetThreadId(int handle, out long threadUid)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@@ -2048,7 +2043,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SetThreadActivity(int handle, bool pause)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetObject<KThread>(handle);
@@ -2062,7 +2057,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
- if (thread == _context.Scheduler.GetCurrentThread())
+ if (thread == KernelStatic.GetCurrentThread())
{
return KernelResult.InvalidThread;
}
@@ -2072,8 +2067,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetThreadContext3(ulong address, int handle)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
+ KThread currentThread = KernelStatic.GetCurrentThread();
KThread thread = currentProcess.HandleTable.GetObject<KThread>(handle);
@@ -2190,13 +2185,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects).Slice(0, handlesCount);
if (handlesCount != 0)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr)
{
@@ -2267,7 +2262,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult CancelSynchronization(int handle)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
+ KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@@ -2293,7 +2288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
}
@@ -2310,7 +2305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
}
@@ -2331,7 +2326,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
mutexAddress,
@@ -2342,7 +2337,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SignalProcessWideKey(ulong address, int count)
{
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
currentProcess.AddressArbiter.SignalProcessWideKey(address, count);
@@ -2361,7 +2356,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
return type switch
{
@@ -2387,7 +2382,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
return type switch
{
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs
index 2d1a3eea..b4e7a0bf 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallHandler.cs
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private void PostSvcHandler()
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.HandlePostSyscall();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/HleCoreManager.cs b/Ryujinx.HLE/HOS/Kernel/Threading/HleCoreManager.cs
deleted file mode 100644
index c2597990..00000000
--- a/Ryujinx.HLE/HOS/Kernel/Threading/HleCoreManager.cs
+++ /dev/null
@@ -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();
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs
deleted file mode 100644
index c4161d54..00000000
--- a/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs
+++ /dev/null
@@ -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);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs
index 2922ee1a..3ddcffc1 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs
@@ -25,14 +25,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter();
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.Success;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!KernelTransfer.UserToKernelInt32(_context, mutexAddress, out int mutexValue))
{
@@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
(KernelResult result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress);
@@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.TimedOut;
@@ -227,7 +227,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
ulong address = requester.MutexAddress;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address))
{
@@ -293,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult WaitForAddressIfEqual(ulong address, int value, long timeout)
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter();
@@ -368,7 +368,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
bool shouldDecrement,
long timeout)
{
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter();
@@ -383,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.TimedOut;
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!KernelTransfer.UserToKernelInt32(_context, address, out int currentValue))
{
@@ -483,7 +483,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address))
{
@@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
offset = 1;
}
- KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address))
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs
index dd7e67ae..d146bff0 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
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();
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs
deleted file mode 100644
index 0aa12b0d..00000000
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs
+++ /dev/null
@@ -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();
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs
index b778c2a4..1d61f2f0 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs
@@ -5,21 +5,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KCriticalSection
{
private readonly KernelContext _context;
-
- public object LockObj { get; private set; }
-
+ private readonly object _lock;
private int _recursionCount;
+ public object Lock => _lock;
+
public KCriticalSection(KernelContext context)
{
_context = context;
-
- LockObj = new object();
+ _lock = new object();
}
public void Enter()
{
- Monitor.Enter(LockObj);
+ Monitor.Enter(_lock);
_recursionCount++;
}
@@ -31,61 +30,34 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return;
}
- bool doContextSwitch = false;
-
if (--_recursionCount == 0)
{
- if (_context.Scheduler.ThreadReselectionRequested)
- {
- _context.Scheduler.SelectThreads();
- }
+ ulong scheduledCoresMask = KScheduler.SelectThreads(_context);
- 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)
- {
- 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
- {
- // Thread running on another core, request a interrupt.
- currentThread.Context.RequestInterrupt();
- }
- }
- }
- }
+ KScheduler.EnableScheduling(_context, scheduledCoresMask);
}
else
{
- doContextSwitch = true;
+ KScheduler.EnableSchedulingFromForeignThread(_context, scheduledCoresMask);
+
+ // 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
{
- Monitor.Exit(LockObj);
- }
-
- if (doContextSwitch)
- {
- _context.Scheduler.ContextSwitch();
+ Monitor.Exit(_lock);
}
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSchedulingData.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
index 83c4a079..2c9d7574 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSchedulingData.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
@@ -1,8 +1,9 @@
using System.Collections.Generic;
+using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
- class KSchedulingData
+ class KPriorityQueue
{
private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
@@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private long[] _scheduledPrioritiesPerCore;
private long[] _suggestedPrioritiesPerCore;
- public KSchedulingData()
+ public KPriorityQueue()
{
_suggestedThreadsPerPrioPerCore = 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];
- int prio = CountTrailingZeros(prioMask);
+ int prio = BitOperations.TrailingZeroCount(prioMask);
prioMask &= ~(1L << prio);
@@ -62,42 +63,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
node = node.Next;
}
- prio = CountTrailingZeros(prioMask);
+ prio = BitOperations.TrailingZeroCount(prioMask);
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)
{
- bool schedulable = thread.DynamicPriority < KScheduler.PrioritiesCount;
-
- int srcCore = thread.CurrentCore;
-
- thread.CurrentCore = dstCore;
-
- if (srcCore == dstCore || !schedulable)
+ int srcCore = thread.ActiveCore;
+ if (srcCore == dstCore)
{
return;
}
+ thread.ActiveCore = dstCore;
+
if (srcCore >= 0)
{
Unschedule(prio, srcCore, thread);
@@ -168,13 +149,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_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);
queue.Remove(thread.SiblingsPerCore[core]);
thread.SiblingsPerCore[core] = queue.AddLast(thread);
+
+ return queue.First.Value;
}
public void Unschedule(int prio, int core, KThread thread)
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index c6da361d..e427f24d 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -1,7 +1,10 @@
+using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Numerics;
+using System.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 CpuCoresCount = 4;
- private const int PreemptionPriorityCores012 = 59;
- private const int PreemptionPriorityCore3 = 63;
+ private const int RoundRobinTimeQuantumMs = 10;
+
+ private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
private readonly KernelContext _context;
+ private readonly int _coreId;
+
+ private struct SchedulingState
+ {
+ public bool NeedsScheduling;
+ public KThread SelectedThread;
+ }
+
+ private SchedulingState _state;
- public KSchedulingData SchedulingData { get; private set; }
+ private AutoResetEvent _idleInterruptEvent;
+ private readonly object _idleInterruptEventLock;
- public KCoreContext[] CoreContexts { get; private set; }
+ private KThread _previousThread;
+ private KThread _currentThread;
+ private readonly KThread _idleThread;
- public bool ThreadReselectionRequested { get; set; }
+ public KThread PreviousThread => _previousThread;
+ public long LastContextSwitchTime { get; private set; }
+ public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning;
- public KScheduler(KernelContext context)
+ public KScheduler(KernelContext context, int coreId)
{
_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;
- for (int core = 0; core < CpuCoresCount; core++)
- {
- CoreContexts[core] = new KCoreContext(this, CoreManager);
- }
+ idleThread.StartHostThread();
+ idleThread.SchedulerWaitEvent.Set();
}
- private void PreemptThreads()
+ private KThread CreateIdleThread(KernelContext context, int cpuCore)
{
- _context.CriticalSection.Enter();
+ KThread idleThread = new KThread(context);
- PreemptThread(PreemptionPriorityCores012, 0);
- PreemptThread(PreemptionPriorityCores012, 1);
- PreemptThread(PreemptionPriorityCores012, 2);
- PreemptThread(PreemptionPriorityCore3, 3);
+ idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop);
- _context.CriticalSection.Leave();
+ return idleThread;
}
- private void PreemptThread(int prio, int core)
+ public static ulong SelectThreads(KernelContext context)
{
- 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)
+ if (context.ThreadReselectionRequested)
{
- SchedulingData.TransferToCore(prio, core, dst);
-
- selectedThread = dst;
+ return SelectThreadsImpl(context);
}
-
- // 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)
+ else
{
- Func<KThread, bool> predicate = x => x.DynamicPriority >= selectedThread.DynamicPriority;
-
- dst = SuitableCandidates().FirstOrDefault(predicate);
-
- if (dst != null)
- {
- SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
- }
+ return 0UL;
}
-
- ThreadReselectionRequested = true;
}
- public void SelectThreads()
+ private static ulong SelectThreadsImpl(KernelContext context)
{
- ThreadReselectionRequested = false;
+ context.ThreadReselectionRequested = false;
+
+ ulong scheduledCoresMask = 0UL;
for (int core = 0; core < CpuCoresCount; core++)
{
- KThread thread = SchedulingData.ScheduledThreads(core).FirstOrDefault();
+ KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
- CoreContexts[core].SelectThread(thread);
+ scheduledCoresMask |= context.Schedulers[core].SelectThread(thread);
}
for (int core = 0; core < CpuCoresCount; core++)
{
// If the core is not idle (there's already a thread running on it),
// then we don't need to attempt load balancing.
- if (SchedulingData.ScheduledThreads(core).Any())
+ if (context.PriorityQueue.ScheduledThreads(core).Any())
{
continue;
}
@@ -146,16 +107,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// Select candidate threads that could run on this core.
// 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;
}
- srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = thread.CurrentCore;
+ srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
}
// Not yet selected candidate found.
@@ -165,9 +125,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// threads, we should skip load balancing entirely.
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;
@@ -179,80 +139,480 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
int srcCore = srcCoresHighestPrioThreads[index];
- KThread src = SchedulingData.ScheduledThreads(srcCore).ElementAtOrDefault(1);
+ KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
if (src != null)
{
// Run the second thread on the queue on the source 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);
}
}
}
+
+ return scheduledCoresMask;
+ }
+
+ private ulong SelectThread(KThread nextThread)
+ {
+ KThread previousThread = _state.SelectedThread;
+
+ if (previousThread != nextThread)
+ {
+ if (previousThread != null)
+ {
+ previousThread.LastScheduledTime = PerformanceCounter.ElapsedTicks;
+ }
+
+ _state.SelectedThread = nextThread;
+ _state.NeedsScheduling = true;
+ return 1UL << _coreId;
+ }
+ else
+ {
+ 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 KThread GetCurrentThread()
+ public void Schedule()
{
- return GetCurrentThreadOrNull() ?? GetDummyThread();
+ _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++)
+ {
+ _context.Schedulers[core]._idleInterruptEvent.Set();
+ }
+
+ KThread nextThread = PickNextThread(selectedThread);
+
+ if (currentThread.Context.Running)
+ {
+ // Wait until this thread is scheduled again, and allow the next thread to run.
+ WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent);
+ }
+ else
+ {
+ // Allow the next thread to run.
+ nextThread.SchedulerWaitEvent.Set();
+
+ // 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;
+ }
}
- public KThread GetCurrentThreadOrNull()
+ private KThread PickNextThread(KThread selectedThread)
{
- lock (CoreContexts)
+ while (true)
{
- for (int core = 0; core < CpuCoresCount; core++)
+ if (selectedThread != null)
{
- if (CoreContexts[core].CurrentThread?.IsCurrentHostThread() ?? false)
+ // 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())
{
- return CoreContexts[core].CurrentThread;
+ SwitchTo(selectedThread);
+
+ if (!_state.NeedsScheduling)
+ {
+ return selectedThread;
+ }
+
+ selectedThread.ThreadContext.Unlock();
}
+ else
+ {
+ 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;
}
- return null;
+ 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;
}
- private KThread _dummyThread;
+ public static void PreemptionThreadLoop(KernelContext context)
+ {
+ while (context.Running)
+ {
+ context.CriticalSection.Enter();
- private KThread GetDummyThread()
+ 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)
{
- if (_dummyThread != null)
+ 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()
{
- return _dummyThread;
+ 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;
+ }
+ }
}
- KProcess dummyProcess = new KProcess(_context);
+ // 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);
- dummyProcess.HandleTable.Initialize(1024);
+ 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 dummyThread = new KThread(_context);
+ KThread nextThread = context.PriorityQueue.Reschedule(currentThread.DynamicPriority, currentThread.ActiveCore, currentThread);
- dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
+ if (nextThread != currentThread)
+ {
+ context.ThreadReselectionRequested = true;
+ }
- return _dummyThread = dummyThread;
+ context.CriticalSection.Leave();
}
- public KProcess GetCurrentProcess()
+ public static void YieldWithLoadBalancing(KernelContext context)
{
- return GetCurrentThread().Owner;
+ 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 void Dispose()
+ public static void YieldToAnyThread(KernelContext context)
{
- Dispose(true);
+ 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();
}
- protected virtual void Dispose(bool disposing)
+ public void Dispose()
{
- if (disposing)
+ // Ensure that the idle thread is not blocked and can exit.
+ lock (_idleInterruptEventLock)
{
- _keepPreempting = false;
+ if (_idleInterruptEvent != null)
+ {
+ _idleInterruptEvent.Set();
+ }
}
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
index 22610b22..419f1536 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
@@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return result;
}
- KThread currentThread = _context.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
if (currentThread.ShallBeTerminated ||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
index f523cb9c..b95b1e8e 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
@@ -4,8 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Numerics;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
@@ -14,17 +13,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
public const int MaxWaitSyncObjects = 64;
- private int _hostThreadRunning;
+ private ManualResetEvent _schedulerWaitEvent;
+
+ public ManualResetEvent SchedulerWaitEvent => _schedulerWaitEvent;
public Thread HostThread { 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 ThreadUid { get; private set; }
- public long TotalTimeRunning { get; set; }
+ private long _totalTimeRunning;
+
+ public long TotalTimeRunning => _totalTimeRunning;
public KSynchronizationObject SignaledObj { get; set; }
@@ -32,6 +38,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private ulong _entrypoint;
private ThreadStart _customThreadStart;
+ private bool _forcedUnschedulable;
+
+ public bool IsSchedulable => _customThreadStart == null && !_forcedUnschedulable;
public ulong MutexAddress { get; set; }
@@ -65,11 +74,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult ObjSyncResult { get; set; }
- public int DynamicPriority { get; set; }
- public int CurrentCore { get; set; }
public int BasePriority { get; set; }
public int PreferredCore { get; set; }
+ public int CurrentCore { get; set; }
+ public int ActiveCore { get; set; }
+
private long _affinityMaskOverride;
private int _preferredCoreOverride;
#pragma warning disable CS0649
@@ -86,26 +96,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
set => _shallBeTerminated = value ? 1 : 0;
}
+ public bool TerminationRequested => ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending;
+
public bool SyncCancelled { get; set; }
public bool WaitingSync { get; set; }
- private bool _hasExited;
+ private int _hasExited;
private bool _hasBeenInitialized;
private bool _hasBeenReleased;
public bool WaitingInArbitration { get; set; }
- private KScheduler _scheduler;
-
- private KSchedulingData _schedulingData;
-
public long LastPc { get; set; }
public KThread(KernelContext context) : base(context)
{
- _scheduler = KernelContext.Scheduler;
- _schedulingData = KernelContext.Scheduler.SchedulingData;
-
WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
WaitSyncHandles = new int[MaxWaitSyncObjects];
@@ -119,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
ulong argsPtr,
ulong stackTop,
int priority,
- int defaultCpuCore,
+ int cpuCore,
KProcess owner,
ThreadType type,
ThreadStart customThreadStart = null)
@@ -129,20 +134,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
throw new ArgumentException($"Invalid thread type \"{type}\".");
}
- PreferredCore = defaultCpuCore;
+ ThreadContext = new KThreadContext();
- AffinityMask |= 1L << defaultCpuCore;
+ PreferredCore = cpuCore;
+ AffinityMask |= 1L << cpuCore;
SchedFlags = type == ThreadType.Dummy
? ThreadSchedState.Running
: ThreadSchedState.None;
- CurrentCore = PreferredCore;
-
+ ActiveCore = cpuCore;
+ ObjSyncResult = KernelResult.ThreadNotStarted;
DynamicPriority = priority;
BasePriority = priority;
-
- ObjSyncResult = KernelResult.ThreadNotStarted;
+ CurrentCore = cpuCore;
_entrypoint = entrypoint;
_customThreadStart = customThreadStart;
@@ -179,41 +184,38 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Context = CpuContext.CreateExecutionContext();
- bool isAarch32 = !Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit);
-
- Context.IsAarch32 = isAarch32;
+ Context.IsAarch32 = !is64Bits;
Context.SetX(0, argsPtr);
- if (isAarch32)
+ if (is64Bits)
{
- Context.SetX(13, (uint)stackTop);
+ Context.SetX(31, stackTop);
}
else
{
- Context.SetX(31, stackTop);
+ Context.SetX(13, (uint)stackTop);
}
Context.CntfrqEl0 = 19200000;
Context.Tpidr = (long)_tlsAddress;
- owner.SubscribeThreadEventHandlers(Context);
-
ThreadUid = KernelContext.NewThreadUid();
- HostThread.Name = $"HLE.HostThread.{ThreadUid}";
+ HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}";
_hasBeenInitialized = true;
if (owner != null)
{
+ owner.SubscribeThreadEventHandlers(Context);
owner.AddThread(this);
if (owner.IsPaused)
{
KernelContext.CriticalSection.Enter();
- if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+ if (TerminationRequested)
{
KernelContext.CriticalSection.Leave();
@@ -237,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
KernelContext.CriticalSection.Enter();
- if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
+ if (!TerminationRequested)
{
_forcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
@@ -253,20 +255,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (!ShallBeTerminated)
{
- KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
- while (SchedFlags != ThreadSchedState.TerminationPending &&
- currentThread.SchedFlags != ThreadSchedState.TerminationPending &&
- !currentThread.ShallBeTerminated)
+ while (SchedFlags != ThreadSchedState.TerminationPending && (currentThread == null || !currentThread.TerminationRequested))
{
if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
{
result = KernelResult.InvalidState;
-
break;
}
- if (currentThread._forcePauseFlags == ThreadSchedState.None)
+ if (currentThread == null || currentThread._forcePauseFlags == ThreadSchedState.None)
{
if (Owner != null && _forcePauseFlags != ThreadSchedState.None)
{
@@ -275,8 +274,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
SetNewSchedFlags(ThreadSchedState.Running);
- result = KernelResult.Success;
+ StartHostThread();
+ result = KernelResult.Success;
break;
}
else
@@ -299,28 +299,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
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()
{
KernelContext.CriticalSection.Enter();
@@ -387,9 +365,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
do
{
- if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+ if (TerminationRequested)
{
- KernelContext.Scheduler.ExitThread(this);
Exit();
// 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();
- if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+ if (TerminationRequested)
{
state = ThreadSchedState.TerminationPending;
}
@@ -416,200 +393,74 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} while (state == ThreadSchedState.TerminationPending);
}
- private void ExitImpl()
+ public void Exit()
{
- KernelContext.CriticalSection.Enter();
-
- SetNewSchedFlags(ThreadSchedState.TerminationPending);
-
- _hasExited = true;
+ // TODO: Debug event.
- Signal();
+ if (Owner != null)
+ {
+ Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
- KernelContext.CriticalSection.Leave();
- }
+ _hasBeenReleased = true;
+ }
- public KernelResult Sleep(long timeout)
- {
KernelContext.CriticalSection.Enter();
- if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
- {
- KernelContext.CriticalSection.Leave();
+ _forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
- return KernelResult.ThreadTerminating;
- }
+ bool decRef = ExitImpl();
- SetNewSchedFlags(ThreadSchedState.Paused);
-
- if (timeout > 0)
- {
- KernelContext.TimeManager.ScheduleFutureInvocation(this, timeout);
- }
+ Context.StopRunning();
KernelContext.CriticalSection.Leave();
- if (timeout > 0)
+ if (decRef)
{
- KernelContext.TimeManager.UnscheduleFutureInvocation(this);
+ DecrementReferenceCount();
}
-
- return 0;
}
- public void Yield()
+ private bool ExitImpl()
{
KernelContext.CriticalSection.Enter();
- if (SchedFlags != ThreadSchedState.Running)
- {
- KernelContext.CriticalSection.Leave();
-
- KernelContext.Scheduler.ContextSwitch();
-
- return;
- }
+ SetNewSchedFlags(ThreadSchedState.TerminationPending);
- if (DynamicPriority < KScheduler.PrioritiesCount)
- {
- // Move current thread to the end of the queue.
- _schedulingData.Reschedule(DynamicPriority, CurrentCore, this);
- }
+ bool decRef = Interlocked.Exchange(ref _hasExited, 1) == 0;
- _scheduler.ThreadReselectionRequested = true;
+ Signal();
KernelContext.CriticalSection.Leave();
- KernelContext.Scheduler.ContextSwitch();
+ return decRef;
}
- public void YieldWithLoadBalancing()
+ public KernelResult Sleep(long timeout)
{
KernelContext.CriticalSection.Enter();
- if (SchedFlags != ThreadSchedState.Running)
+ if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
{
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;
- }
- }
+ return KernelResult.ThreadTerminating;
}
- KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
-
- if (dst != null)
- {
- _schedulingData.TransferToCore(dst.DynamicPriority, core, dst);
-
- _scheduler.ThreadReselectionRequested = true;
- }
+ SetNewSchedFlags(ThreadSchedState.Paused);
- if (this != nextThreadOnCurrentQueue)
+ if (timeout > 0)
{
- _scheduler.ThreadReselectionRequested = true;
+ KernelContext.TimeManager.ScheduleFutureInvocation(this, timeout);
}
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)
+ if (timeout > 0)
{
- _scheduler.ThreadReselectionRequested = true;
+ KernelContext.TimeManager.UnscheduleFutureInvocation(this);
}
- KernelContext.CriticalSection.Leave();
-
- KernelContext.Scheduler.ContextSwitch();
+ return 0;
}
public void SetPriority(int priority)
@@ -751,17 +602,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
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)
{
- CurrentCore = HighestSetCore(AffinityMask);
+ ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask);
}
else
{
- CurrentCore = PreferredCore;
+ ActiveCore = PreferredCore;
}
}
@@ -774,19 +625,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
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()
{
ThreadSchedState oldFlags = SchedFlags;
@@ -995,92 +833,112 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
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)
{
// 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++)
{
- 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)
{
// 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++)
{
- 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)
{
- if (SchedFlags != ThreadSchedState.Running)
+ if (SchedFlags != ThreadSchedState.Running || !IsSchedulable)
{
return;
}
// 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++)
{
- 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.
- KThread currentThread = _scheduler.GetCurrentThread();
+ KThread currentThread = KernelStatic.GetCurrentThread();
- if (CurrentCore >= 0)
+ if (ActiveCore >= 0)
{
if (currentThread == this)
{
- _schedulingData.SchedulePrepend(DynamicPriority, CurrentCore, this);
+ KernelContext.PriorityQueue.SchedulePrepend(DynamicPriority, ActiveCore, this);
}
else
{
- _schedulingData.Schedule(DynamicPriority, CurrentCore, this);
+ KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this);
}
}
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)
{
- if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount)
+ if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount || !IsSchedulable)
{
return;
}
@@ -1092,11 +950,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
if (core == oldCore)
{
- _schedulingData.Unschedule(DynamicPriority, core, this);
+ KernelContext.PriorityQueue.Unschedule(DynamicPriority, core, this);
}
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 (core == CurrentCore)
+ if (core == ActiveCore)
{
- _schedulingData.Schedule(DynamicPriority, core, this);
+ KernelContext.PriorityQueue.Schedule(DynamicPriority, core, this);
}
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)
@@ -1141,17 +999,32 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Logger.Info?.Print(LogClass.Cpu, $"Guest stack trace:\n{GetGuestStackTrace()}\n");
}
- public void Execute()
+ public void AddCpuTime(long ticks)
+ {
+ Interlocked.Add(ref _totalTimeRunning, ticks);
+ }
+
+ public void StartHostThread()
{
- if (Interlocked.CompareExchange(ref _hostThreadRunning, 1, 0) == 0)
+ if (_schedulerWaitEvent == null)
{
- HostThread.Start();
+ var schedulerWaitEvent = new ManualResetEvent(false);
+
+ if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null)
+ {
+ HostThread.Start();
+ }
+ else
+ {
+ schedulerWaitEvent.Dispose();
+ }
}
}
private void ThreadStart()
{
- KernelStatic.SetKernelContext(KernelContext);
+ _schedulerWaitEvent.WaitOne();
+ KernelStatic.SetKernelContext(KernelContext, this);
if (_customThreadStart != null)
{
@@ -1162,20 +1035,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Owner.Context.Execute(Context, _entrypoint);
}
- KernelContext.Scheduler.ExitThread(this);
- KernelContext.Scheduler.RemoveThread(this);
-
Context.Dispose();
+ _schedulerWaitEvent.Dispose();
}
- public bool IsCurrentHostThread()
+ public void MakeUnschedulable()
{
- return Thread.CurrentThread == HostThread;
+ _forcedUnschedulable = true;
}
public override bool IsSignaled()
{
- return _hasExited;
+ return _hasExited != 0;
}
protected override void Destroy()
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs
new file mode 100644
index 00000000..a7e9c4b3
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs
@@ -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);
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index f70d930f..3161e689 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services
};
private readonly KernelContext _context;
- private readonly KProcess _selfProcess;
+ private KProcess _selfProcess;
private readonly List<int> _sessionHandles = 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);
- context.Syscall.CreateProcess(creationInfo, DefaultCapabilities, out int handle, null, ServerLoop);
-
- _selfProcess = context.Scheduler.GetCurrentProcess().HandleTable.GetKProcess(handle);
-
- context.Syscall.StartProcess(handle, 44, 3, 0x1000);
+ KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
}
private void AddPort(int serverPortHandle, IpcService obj)
@@ -82,6 +78,8 @@ namespace Ryujinx.HLE.HOS.Services
private void ServerLoop()
{
+ _selfProcess = KernelStatic.GetCurrentProcess();
+
if (SmObject != null)
{
_context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
@@ -95,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services
InitDone.Dispose();
}
- KThread thread = _context.Scheduler.GetCurrentThread();
+ KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress;
_context.Syscall.SetHeapSize(0x200000, out ulong heapAddr);
@@ -107,18 +105,14 @@ namespace Ryujinx.HLE.HOS.Services
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++)
- {
- if (_context.Syscall.AcceptSession(handles[i], out int serverSessionHandle) == KernelResult.Success)
- {
- AddSessionObj(serverSessionHandle, _ports[handles[i]]);
- }
- }
-
- handles = _sessionHandles.ToArray();
+ portHandles.CopyTo(handles, 0);
+ sessionHandles.CopyTo(handles, portHandles.Length);
+ // 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);
thread.HandlePostSyscall();
@@ -130,8 +124,9 @@ namespace Ryujinx.HLE.HOS.Services
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];
if (Process(signaledHandle, heapAddr))
@@ -141,6 +136,15 @@ namespace Ryujinx.HLE.HOS.Services
}
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 + 0x4, 2 << 10);
_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)
{
- KProcess process = _context.Scheduler.GetCurrentProcess();
- KThread thread = _context.Scheduler.GetCurrentThread();
+ KProcess process = KernelStatic.GetCurrentProcess();
+ KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress;
ulong messageSize = 0x100;
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
index 389a980b..1043dac9 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
@@ -184,11 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public void WaitDequeueEvent()
{
- Monitor.Exit(Lock);
-
- KernelStatic.YieldUntilCompletion(WaitForLock);
-
- Monitor.Enter(Lock);
+ WaitForLock();
}
public void SignalIsAllocatingEvent()
@@ -198,21 +194,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public void WaitIsAllocatingEvent()
{
- Monitor.Exit(Lock);
-
- KernelStatic.YieldUntilCompletion(WaitForLock);
-
- Monitor.Enter(Lock);
+ WaitForLock();
}
private void WaitForLock()
{
- lock (Lock)
+ if (Active)
{
- if (Active)
- {
- Monitor.Wait(Lock);
- }
+ Monitor.Wait(Lock);
}
}
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 2c58898c..d54c64e1 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -115,11 +115,6 @@ namespace Ryujinx.HLE
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
- if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
- {
- System.EnableMultiCoreScheduling();
- }
-
System.EnablePtc = ConfigurationState.Instance.System.EnablePtc;
System.FsIntegrityCheckLevel = GetIntegrityCheckLevel();
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 293a8dca..8990bd98 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,4 +1,4 @@
-{
+{
"version": 17,
"res_scale": 1,
"res_scale_custom": 1,
@@ -22,7 +22,6 @@
"check_updates_on_start": true,
"enable_vsync": true,
"enable_shader_cache": true,
- "enable_multicore_scheduling": true,
"enable_ptc": false,
"enable_fs_integrity_checks": true,
"fs_global_access_log_mode": 0,
diff --git a/Ryujinx/THIRDPARTY.md b/Ryujinx/THIRDPARTY.md
index 94b7ec37..c7015deb 100644
--- a/Ryujinx/THIRDPARTY.md
+++ b/Ryujinx/THIRDPARTY.md
@@ -200,4 +200,29 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
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.
``` \ No newline at end of file
diff --git a/Ryujinx/Ui/SettingsWindow.cs b/Ryujinx/Ui/SettingsWindow.cs
index 100246bc..07878ee4 100644
--- a/Ryujinx/Ui/SettingsWindow.cs
+++ b/Ryujinx/Ui/SettingsWindow.cs
@@ -43,7 +43,6 @@ namespace Ryujinx.Ui
[GUI] CheckButton _checkUpdatesToggle;
[GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _shaderCacheToggle;
- [GUI] CheckButton _multiSchedToggle;
[GUI] CheckButton _ptcToggle;
[GUI] CheckButton _fsicToggle;
[GUI] CheckButton _ignoreToggle;
@@ -188,11 +187,6 @@ namespace Ryujinx.Ui
_shaderCacheToggle.Click();
}
- if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
- {
- _multiSchedToggle.Click();
- }
-
if (ConfigurationState.Instance.System.EnablePtc)
{
_ptcToggle.Click();
@@ -401,7 +395,6 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active;
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.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.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
@@ -490,7 +483,7 @@ namespace Ryujinx.Ui
foreach (string directory in fileChooser.Filenames)
{
bool directoryAdded = false;
-
+
if (_gameDirsBoxStore.GetIterFirst(out TreeIter treeIter))
{
do
diff --git a/Ryujinx/Ui/SettingsWindow.glade b/Ryujinx/Ui/SettingsWindow.glade
index ebc3fcb1..1090bf2c 100644
--- a/Ryujinx/Ui/SettingsWindow.glade
+++ b/Ryujinx/Ui/SettingsWindow.glade
@@ -1440,24 +1440,6 @@
</packing>
</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>
<object class="GtkCheckButton" id="_ptcToggle">
<property name="label" translatable="yes">Enable Profiled Persistent Translation Cache</property>
<property name="visible">True</property>
diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json
index 1602b48a..fb055c0d 100644
--- a/Ryujinx/_schema.json
+++ b/Ryujinx/_schema.json
@@ -18,7 +18,6 @@
"system_region",
"docked_mode",
"enable_vsync",
- "enable_multicore_scheduling",
"enable_ptc",
"enable_fs_integrity_checks",
"fs_global_access_log_mode",
@@ -1196,17 +1195,6 @@
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": {
"$id": "#/properties/enable_ptc",
"type": "boolean",