Rewrite scheduler context switch code (#1786)
* Rewrite scheduler context switch code * Fix race in UnmapIpcRestorePermission * Fix thread exit issue that could leave the scheduler in a invalid state * Change context switch method to not wait on guest thread, remove spin wait, use SignalAndWait to pass control * Remove multi-core setting (it is always on now) * Re-enable assert * Remove multicore from default config and schema * Fix race in KTimeManager
This commit is contained in:
parent
3484265d37
commit
48278905d1
37 changed files with 1080 additions and 1160 deletions
|
@ -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,17 +393,46 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
} while (state == ThreadSchedState.TerminationPending);
|
||||
}
|
||||
|
||||
private void ExitImpl()
|
||||
public void Exit()
|
||||
{
|
||||
// TODO: Debug event.
|
||||
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
|
||||
|
||||
_hasBeenReleased = true;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
|
||||
|
||||
bool decRef = ExitImpl();
|
||||
|
||||
Context.StopRunning();
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
if (decRef)
|
||||
{
|
||||
DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExitImpl()
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
SetNewSchedFlags(ThreadSchedState.TerminationPending);
|
||||
|
||||
_hasExited = true;
|
||||
bool decRef = Interlocked.Exchange(ref _hasExited, 1) == 0;
|
||||
|
||||
Signal();
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return decRef;
|
||||
}
|
||||
|
||||
public KernelResult Sleep(long timeout)
|
||||
|
@ -457,161 +463,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
return 0;
|
||||
}
|
||||
|
||||
public void Yield()
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (SchedFlags != ThreadSchedState.Running)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
KernelContext.Scheduler.ContextSwitch();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (DynamicPriority < KScheduler.PrioritiesCount)
|
||||
{
|
||||
// Move current thread to the end of the queue.
|
||||
_schedulingData.Reschedule(DynamicPriority, CurrentCore, this);
|
||||
}
|
||||
|
||||
_scheduler.ThreadReselectionRequested = true;
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
KernelContext.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void YieldWithLoadBalancing()
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (SchedFlags != ThreadSchedState.Running)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
KernelContext.Scheduler.ContextSwitch();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int prio = DynamicPriority;
|
||||
int core = CurrentCore;
|
||||
|
||||
KThread nextThreadOnCurrentQueue = null;
|
||||
|
||||
if (DynamicPriority < KScheduler.PrioritiesCount)
|
||||
{
|
||||
// Move current thread to the end of the queue.
|
||||
_schedulingData.Reschedule(prio, core, this);
|
||||
|
||||
Func<KThread, bool> predicate = x => x.DynamicPriority == prio;
|
||||
|
||||
nextThreadOnCurrentQueue = _schedulingData.ScheduledThreads(core).FirstOrDefault(predicate);
|
||||
}
|
||||
|
||||
IEnumerable<KThread> SuitableCandidates()
|
||||
{
|
||||
foreach (KThread thread in _schedulingData.SuggestedThreads(core))
|
||||
{
|
||||
int srcCore = thread.CurrentCore;
|
||||
|
||||
if (srcCore >= 0)
|
||||
{
|
||||
KThread selectedSrcCore = _scheduler.CoreContexts[srcCore].SelectedThread;
|
||||
|
||||
if (selectedSrcCore == thread || ((selectedSrcCore?.DynamicPriority ?? 2) < 2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the candidate was scheduled after the current thread, then it's not worth it,
|
||||
// unless the priority is higher than the current one.
|
||||
if (nextThreadOnCurrentQueue.LastScheduledTime >= thread.LastScheduledTime ||
|
||||
nextThreadOnCurrentQueue.DynamicPriority < thread.DynamicPriority)
|
||||
{
|
||||
yield return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
|
||||
|
||||
if (dst != null)
|
||||
{
|
||||
_schedulingData.TransferToCore(dst.DynamicPriority, core, dst);
|
||||
|
||||
_scheduler.ThreadReselectionRequested = true;
|
||||
}
|
||||
|
||||
if (this != nextThreadOnCurrentQueue)
|
||||
{
|
||||
_scheduler.ThreadReselectionRequested = true;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
KernelContext.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void YieldAndWaitForLoadBalancing()
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (SchedFlags != ThreadSchedState.Running)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
KernelContext.Scheduler.ContextSwitch();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int core = CurrentCore;
|
||||
|
||||
_schedulingData.TransferToCore(DynamicPriority, -1, this);
|
||||
|
||||
KThread selectedThread = null;
|
||||
|
||||
if (!_schedulingData.ScheduledThreads(core).Any())
|
||||
{
|
||||
foreach (KThread thread in _schedulingData.SuggestedThreads(core))
|
||||
{
|
||||
if (thread.CurrentCore < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
KThread firstCandidate = _schedulingData.ScheduledThreads(thread.CurrentCore).FirstOrDefault();
|
||||
|
||||
if (firstCandidate == thread)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstCandidate == null || firstCandidate.DynamicPriority >= 2)
|
||||
{
|
||||
_schedulingData.TransferToCore(thread.DynamicPriority, core, thread);
|
||||
|
||||
selectedThread = thread;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedThread != this)
|
||||
{
|
||||
_scheduler.ThreadReselectionRequested = true;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
KernelContext.Scheduler.ContextSwitch();
|
||||
}
|
||||
|
||||
public void SetPriority(int priority)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
@ -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)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _hostThreadRunning, 1, 0) == 0)
|
||||
Interlocked.Add(ref _totalTimeRunning, ticks);
|
||||
}
|
||||
|
||||
public void StartHostThread()
|
||||
{
|
||||
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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue