| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- 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 readonly KernelContext _context;
- public KSchedulingData SchedulingData { get; private set; }
- public KCoreContext[] CoreContexts { get; private set; }
- public bool ThreadReselectionRequested { get; set; }
- public KScheduler(KernelContext context)
- {
- _context = context;
- 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()
- {
- _context.CriticalSection.Enter();
- PreemptThread(PreemptionPriorityCores012, 0);
- PreemptThread(PreemptionPriorityCores012, 1);
- PreemptThread(PreemptionPriorityCores012, 2);
- PreemptThread(PreemptionPriorityCore3, 3);
- _context.CriticalSection.Leave();
- }
- private void PreemptThread(int prio, int core)
- {
- IEnumerable<KThread> scheduledThreads = SchedulingData.ScheduledThreads(core);
- KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
- // Yield priority queue.
- if (selectedThread != null)
- {
- SchedulingData.Reschedule(prio, core, selectedThread);
- }
- IEnumerable<KThread> SuitableCandidates()
- {
- foreach (KThread thread in SchedulingData.SuggestedThreads(core))
- {
- int srcCore = thread.CurrentCore;
- if (srcCore >= 0)
- {
- KThread highestPrioSrcCore = SchedulingData.ScheduledThreads(srcCore).FirstOrDefault();
- if (highestPrioSrcCore != null && highestPrioSrcCore.DynamicPriority < 2)
- {
- break;
- }
- if (highestPrioSrcCore == thread)
- {
- continue;
- }
- }
- // If the candidate was scheduled after the current thread, then it's not worth it.
- if (selectedThread == null || selectedThread.LastScheduledTime >= thread.LastScheduledTime)
- {
- yield return thread;
- }
- }
- }
- // Select candidate threads that could run on this core.
- // Only take into account threads that are not yet selected.
- KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
- if (dst != null)
- {
- SchedulingData.TransferToCore(prio, core, dst);
- selectedThread = dst;
- }
- // If the priority of the currently selected thread is lower than preemption priority,
- // then allow threads with lower priorities to be selected aswell.
- if (selectedThread != null && selectedThread.DynamicPriority > prio)
- {
- Func<KThread, bool> predicate = x => x.DynamicPriority >= selectedThread.DynamicPriority;
- dst = SuitableCandidates().FirstOrDefault(predicate);
- if (dst != null)
- {
- SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
- }
- }
- ThreadReselectionRequested = true;
- }
- public void SelectThreads()
- {
- ThreadReselectionRequested = false;
- for (int core = 0; core < CpuCoresCount; core++)
- {
- KThread thread = SchedulingData.ScheduledThreads(core).FirstOrDefault();
- CoreContexts[core].SelectThread(thread);
- }
- for (int core = 0; core < CpuCoresCount; core++)
- {
- // 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 candidates 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?.IsCurrentHostThread() ?? false)
- {
- return CoreContexts[core].CurrentThread;
- }
- }
- }
- return GetDummyThread();
- throw new InvalidOperationException("Current thread is not scheduled!");
- }
- private KThread _dummyThread;
- private KThread GetDummyThread()
- {
- if (_dummyThread != null)
- {
- return _dummyThread;
- }
- KProcess dummyProcess = new KProcess(_context);
- KThread dummyThread = new KThread(_context);
- dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
- return _dummyThread = dummyThread;
- }
- public KProcess GetCurrentProcess()
- {
- return GetCurrentThread().Owner;
- }
- public void Dispose()
- {
- Dispose(true);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _keepPreempting = false;
- }
- }
- }
- }
|