mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-06-02 13:18:46 +02:00
Merge c0c81d4c9d
into eb1ce41b00
This commit is contained in:
commit
5a6fc17453
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -28,13 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
private SchedulingState _state;
|
||||
|
||||
private AutoResetEvent _idleInterruptEvent;
|
||||
private readonly object _idleInterruptEventLock;
|
||||
|
||||
private KThread _previousThread;
|
||||
private KThread _currentThread;
|
||||
private readonly KThread _idleThread;
|
||||
|
||||
private int _coreIdleLock;
|
||||
private bool _idleSignalled = true;
|
||||
private bool _idleActive = true;
|
||||
|
||||
public KThread PreviousThread => _previousThread;
|
||||
public KThread CurrentThread => _currentThread;
|
||||
public long LastContextSwitchTime { get; private set; }
|
||||
|
@ -45,23 +47,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
_context = context;
|
||||
_coreId = coreId;
|
||||
|
||||
_idleInterruptEvent = new AutoResetEvent(false);
|
||||
_idleInterruptEventLock = new object();
|
||||
|
||||
KThread idleThread = CreateIdleThread(context, coreId);
|
||||
|
||||
_currentThread = idleThread;
|
||||
_idleThread = idleThread;
|
||||
|
||||
idleThread.StartHostThread();
|
||||
idleThread.SchedulerWaitEvent.Set();
|
||||
}
|
||||
|
||||
private KThread CreateIdleThread(KernelContext context, int cpuCore)
|
||||
{
|
||||
KThread idleThread = new(context);
|
||||
|
||||
idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop);
|
||||
idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, null);
|
||||
|
||||
return idleThread;
|
||||
}
|
||||
|
@ -243,33 +239,57 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
}
|
||||
|
||||
// If the core is idle, ensure that the idle thread is awaken.
|
||||
context.Schedulers[coreToSignal]._idleInterruptEvent.Set();
|
||||
context.Schedulers[coreToSignal].NotifyIdleThread();
|
||||
|
||||
scheduledCoresMask &= ~(1UL << coreToSignal);
|
||||
}
|
||||
}
|
||||
|
||||
private void IdleThreadLoop()
|
||||
private void ActivateIdleThread()
|
||||
{
|
||||
while (_context.Running)
|
||||
while (Interlocked.CompareExchange(ref _coreIdleLock, 1, 0) != 0)
|
||||
{ }
|
||||
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
// Signals that idle thread is now active on this core.
|
||||
_idleActive = true;
|
||||
|
||||
TryLeaveIdle();
|
||||
|
||||
Interlocked.Exchange(ref _coreIdleLock, 0);
|
||||
}
|
||||
|
||||
private void NotifyIdleThread()
|
||||
{
|
||||
while (Interlocked.CompareExchange(ref _coreIdleLock, 1, 0) != 0)
|
||||
{ }
|
||||
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
// Signals that the idle core may be able to exit idle.
|
||||
_idleSignalled = true;
|
||||
|
||||
TryLeaveIdle();
|
||||
|
||||
Interlocked.Exchange(ref _coreIdleLock, 0);
|
||||
}
|
||||
|
||||
public void TryLeaveIdle()
|
||||
{
|
||||
if (_idleSignalled && _idleActive)
|
||||
{
|
||||
_state.NeedsScheduling = false;
|
||||
Thread.MemoryBarrier();
|
||||
KThread nextThread = PickNextThread(_state.SelectedThread);
|
||||
KThread nextThread = PickNextThread(_idleThread, _state.SelectedThread);
|
||||
|
||||
if (_idleThread != nextThread)
|
||||
{
|
||||
_idleThread.SchedulerWaitEvent.Reset();
|
||||
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, _idleThread.SchedulerWaitEvent);
|
||||
_idleActive = false;
|
||||
nextThread.SchedulerWaitEvent.Set();
|
||||
}
|
||||
|
||||
_idleInterruptEvent.WaitOne();
|
||||
}
|
||||
|
||||
lock (_idleInterruptEventLock)
|
||||
{
|
||||
_idleInterruptEvent.Dispose();
|
||||
_idleInterruptEvent = null;
|
||||
_idleSignalled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,26 +306,45 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(currentThread != _idleThread);
|
||||
|
||||
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();
|
||||
_context.Schedulers[core].NotifyIdleThread();
|
||||
}
|
||||
|
||||
KThread nextThread = PickNextThread(selectedThread);
|
||||
KThread nextThread = PickNextThread(KernelStatic.GetCurrentThread(), 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);
|
||||
|
||||
if (nextThread == _idleThread)
|
||||
{
|
||||
ActivateIdleThread();
|
||||
currentThread.SchedulerWaitEvent.WaitOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allow the next thread to run.
|
||||
nextThread.SchedulerWaitEvent.Set();
|
||||
|
||||
if (nextThread == _idleThread)
|
||||
{
|
||||
ActivateIdleThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
|
@ -319,7 +358,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
}
|
||||
}
|
||||
|
||||
private KThread PickNextThread(KThread selectedThread)
|
||||
private KThread PickNextThread(KThread currentThread, KThread selectedThread)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
@ -335,7 +374,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
// on the core, as the scheduled thread will handle the next switch.
|
||||
if (selectedThread.ThreadContext.Lock())
|
||||
{
|
||||
SwitchTo(selectedThread);
|
||||
SwitchTo(currentThread, selectedThread);
|
||||
|
||||
if (!_state.NeedsScheduling)
|
||||
{
|
||||
|
@ -353,7 +392,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
{
|
||||
// The core is idle now, make sure that the idle thread can run
|
||||
// and switch the core when a thread is available.
|
||||
SwitchTo(null);
|
||||
SwitchTo(currentThread, null);
|
||||
return _idleThread;
|
||||
}
|
||||
|
||||
|
@ -363,10 +402,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
}
|
||||
}
|
||||
|
||||
private void SwitchTo(KThread nextThread)
|
||||
private void SwitchTo(KThread currentThread, KThread nextThread)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
KProcess currentProcess = currentThread.Owner;
|
||||
|
||||
nextThread ??= _idleThread;
|
||||
|
||||
|
@ -645,11 +683,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
// Ensure that the idle thread is not blocked and can exit.
|
||||
lock (_idleInterruptEventLock)
|
||||
{
|
||||
_idleInterruptEvent?.Set();
|
||||
}
|
||||
// No resources to dispose for now.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -44,8 +45,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
private ulong _entrypoint;
|
||||
private ThreadStart _customThreadStart;
|
||||
private bool _forcedUnschedulable;
|
||||
private bool _isDummy;
|
||||
|
||||
public bool IsSchedulable => _customThreadStart == null && !_forcedUnschedulable;
|
||||
public bool IsSchedulable => _customThreadStart == null && !_isDummy && !_forcedUnschedulable;
|
||||
|
||||
public ulong MutexAddress { get; set; }
|
||||
public int KernelWaitersCount { get; private set; }
|
||||
|
@ -143,7 +145,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
PreferredCore = cpuCore;
|
||||
AffinityMask |= 1UL << cpuCore;
|
||||
|
||||
SchedFlags = type == ThreadType.Dummy
|
||||
_isDummy = type == ThreadType.Dummy;
|
||||
|
||||
SchedFlags = _isDummy
|
||||
? ThreadSchedState.Running
|
||||
: ThreadSchedState.None;
|
||||
|
||||
|
@ -183,7 +187,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
is64Bits = true;
|
||||
}
|
||||
|
||||
HostThread = new Thread(ThreadStart);
|
||||
if (!_isDummy)
|
||||
{
|
||||
HostThread = new Thread(ThreadStart);
|
||||
}
|
||||
|
||||
Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext();
|
||||
|
||||
|
@ -207,7 +214,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
ThreadUid = KernelContext.NewThreadUid();
|
||||
|
||||
HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}";
|
||||
if (!_isDummy)
|
||||
{
|
||||
HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}";
|
||||
}
|
||||
|
||||
_hasBeenInitialized = true;
|
||||
|
||||
|
@ -1055,6 +1065,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
// 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.
|
||||
|
||||
Debug.Assert(!_isDummy);
|
||||
|
||||
if (SchedFlags == ThreadSchedState.Running)
|
||||
{
|
||||
_schedulerWaitEvent.Set();
|
||||
|
@ -1231,6 +1244,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public void StartHostThread()
|
||||
{
|
||||
if (_isDummy)
|
||||
{
|
||||
throw new InvalidOperationException("Dummy threads can't start a host thread.");
|
||||
}
|
||||
|
||||
if (_schedulerWaitEvent == null)
|
||||
{
|
||||
var schedulerWaitEvent = new ManualResetEvent(false);
|
||||
|
|
|
@ -515,11 +515,14 @@ namespace Ryujinx.HLE.HOS.Services
|
|||
{
|
||||
if (disposing && _selfThread != null)
|
||||
{
|
||||
if (_selfThread.HostThread.ManagedThreadId != Environment.CurrentManagedThreadId && _selfThread.HostThread.Join(_threadJoinTimeout) == false)
|
||||
if (_selfThread.HostThread != null)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Service, $"The ServerBase thread didn't terminate within {_threadJoinTimeout:g}, waiting longer.");
|
||||
if (_selfThread.HostThread.ManagedThreadId != Environment.CurrentManagedThreadId && _selfThread.HostThread.Join(_threadJoinTimeout) == false)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Service, $"The ServerBase thread didn't terminate within {_threadJoinTimeout:g}, waiting longer.");
|
||||
|
||||
_selfThread.HostThread.Join(Timeout.Infinite);
|
||||
_selfThread.HostThread.Join(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
if (Interlocked.Exchange(ref _isDisposed, 1) == 0)
|
||||
|
|
Loading…
Reference in New Issue
Block a user