aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-12-18 03:33:36 -0200
committerGitHub <noreply@github.com>2018-12-18 03:33:36 -0200
commit0039bb639493b2d1e2764cae380311ba8e87704b (patch)
tree63a912a95c8261775c2acb8a5b9ca0f10ad4ae33 /Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
parent2534a7f10c627810e6e0272b4cc9758e90f733c1 (diff)
Refactor SVC handler (#540)
* Refactor SVC handler * Get rid of KernelErr * Split kernel code files into multiple folders
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs234
1 files changed, 234 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
new file mode 100644
index 00000000..60e15efa
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -0,0 +1,234 @@
+using Ryujinx.HLE.HOS.Kernel.Process;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Ryujinx.HLE.HOS.Kernel.Threading
+{
+ partial class KScheduler : IDisposable
+ {
+ public const int PrioritiesCount = 64;
+ public const int CpuCoresCount = 4;
+
+ private const int PreemptionPriorityCores012 = 59;
+ private const int PreemptionPriorityCore3 = 63;
+
+ private Horizon _system;
+
+ public KSchedulingData SchedulingData { get; private set; }
+
+ public KCoreContext[] CoreContexts { get; private set; }
+
+ public bool ThreadReselectionRequested { get; set; }
+
+ public KScheduler(Horizon system)
+ {
+ _system = system;
+
+ SchedulingData = new KSchedulingData();
+
+ CoreManager = new HleCoreManager();
+
+ CoreContexts = new KCoreContext[CpuCoresCount];
+
+ for (int core = 0; core < CpuCoresCount; core++)
+ {
+ CoreContexts[core] = new KCoreContext(this, CoreManager);
+ }
+ }
+
+ private void PreemptThreads()
+ {
+ _system.CriticalSection.Enter();
+
+ PreemptThread(PreemptionPriorityCores012, 0);
+ PreemptThread(PreemptionPriorityCores012, 1);
+ PreemptThread(PreemptionPriorityCores012, 2);
+ PreemptThread(PreemptionPriorityCore3, 3);
+
+ _system.CriticalSection.Leave();
+ }
+
+ private void PreemptThread(int prio, int core)
+ {
+ IEnumerable<KThread> scheduledThreads = SchedulingData.ScheduledThreads(core);
+
+ KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
+
+ //Yield priority queue.
+ if (selectedThread != null)
+ {
+ SchedulingData.Reschedule(prio, core, selectedThread);
+ }
+
+ IEnumerable<KThread> SuitableCandidates()
+ {
+ foreach (KThread thread in SchedulingData.SuggestedThreads(core))
+ {
+ int srcCore = thread.CurrentCore;
+
+ if (srcCore >= 0)
+ {
+ KThread highestPrioSrcCore = SchedulingData.ScheduledThreads(srcCore).FirstOrDefault();
+
+ if (highestPrioSrcCore != null && highestPrioSrcCore.DynamicPriority < 2)
+ {
+ break;
+ }
+
+ if (highestPrioSrcCore == thread)
+ {
+ continue;
+ }
+ }
+
+ //If the candidate was scheduled after the current thread, then it's not worth it.
+ if (selectedThread == null || selectedThread.LastScheduledTime >= thread.LastScheduledTime)
+ {
+ yield return thread;
+ }
+ }
+ }
+
+ //Select candidate threads that could run on this core.
+ //Only take into account threads that are not yet selected.
+ KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
+
+ if (dst != null)
+ {
+ SchedulingData.TransferToCore(prio, core, dst);
+
+ selectedThread = dst;
+ }
+
+ //If the priority of the currently selected thread is lower than preemption priority,
+ //then allow threads with lower priorities to be selected aswell.
+ if (selectedThread != null && selectedThread.DynamicPriority > prio)
+ {
+ Func<KThread, bool> predicate = x => x.DynamicPriority >= selectedThread.DynamicPriority;
+
+ dst = SuitableCandidates().FirstOrDefault(predicate);
+
+ if (dst != null)
+ {
+ SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
+ }
+ }
+
+ ThreadReselectionRequested = true;
+ }
+
+ public void SelectThreads()
+ {
+ ThreadReselectionRequested = false;
+
+ for (int core = 0; core < CpuCoresCount; core++)
+ {
+ KThread thread = SchedulingData.ScheduledThreads(core).FirstOrDefault();
+
+ CoreContexts[core].SelectThread(thread);
+ }
+
+ for (int core = 0; core < CpuCoresCount; core++)
+ {
+ //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())
+ {
+ continue;
+ }
+
+ int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
+
+ int srcCoresHighestPrioThreadsCount = 0;
+
+ KThread dst = null;
+
+ //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))
+ {
+ if (thread.CurrentCore < 0 || thread != CoreContexts[thread.CurrentCore].SelectedThread)
+ {
+ dst = thread;
+
+ break;
+ }
+
+ srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = thread.CurrentCore;
+ }
+
+ //Not yet selected candidate found.
+ if (dst != null)
+ {
+ //Priorities < 2 are used for the kernel message dispatching
+ //threads, we should skip load balancing entirely.
+ if (dst.DynamicPriority >= 2)
+ {
+ SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
+
+ CoreContexts[core].SelectThread(dst);
+ }
+
+ continue;
+ }
+
+ //All candiates are already selected, choose the best one
+ //(the first one that doesn't make the source core idle if moved).
+ for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
+ {
+ int srcCore = srcCoresHighestPrioThreads[index];
+
+ KThread src = SchedulingData.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;
+
+ CoreContexts[srcCore].SelectThread(src);
+
+ SchedulingData.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc);
+
+ CoreContexts[core].SelectThread(origSelectedCoreSrc);
+ }
+ }
+ }
+ }
+
+ public KThread GetCurrentThread()
+ {
+ lock (CoreContexts)
+ {
+ for (int core = 0; core < CpuCoresCount; core++)
+ {
+ if (CoreContexts[core].CurrentThread?.Context.IsCurrentThread() ?? false)
+ {
+ return CoreContexts[core].CurrentThread;
+ }
+ }
+ }
+
+ throw new InvalidOperationException("Current thread is not scheduled!");
+ }
+
+ public KProcess GetCurrentProcess()
+ {
+ return GetCurrentThread().Owner;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _keepPreempting = false;
+ }
+ }
+ }
+} \ No newline at end of file