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:
gdkchan 2020-12-09 19:20:05 -03:00 committed by GitHub
parent 3484265d37
commit 48278905d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1080 additions and 1160 deletions

View file

@ -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()
{
}
}
}

View file

@ -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,6 +51,57 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_waitEvent.Set();
}
public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj)
{
lock (_context.CriticalSection.Lock)
{
_waitingObjects.RemoveAll(x => x.Object == schedulerObj);
}
}
private void WaitAndCheckScheduledObjects()
{
using (_waitEvent = new AutoResetEvent(false))
{
while (_keepRunning)
{
WaitingObject next;
lock (_context.CriticalSection.Lock)
{
next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
}
if (next != null)
{
long timePoint = PerformanceCounter.ElapsedMilliseconds;
if (next.TimePoint > timePoint)
{
_waitEvent.WaitOne((int)(next.TimePoint - timePoint));
}
bool timeUp = PerformanceCounter.ElapsedMilliseconds >= next.TimePoint;
if (timeUp)
{
lock (_context.CriticalSection.Lock)
{
if (_waitingObjects.Remove(next))
{
next.Object.TimeUp();
}
}
}
}
else
{
_waitEvent.WaitOne();
}
}
}
}
public static long ConvertNanosecondsToMilliseconds(long time)
{
time /= 1000000;
@ -70,77 +119,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
return time * 1000000;
}
public static long ConvertMillisecondsToTicks(long time)
public static long ConvertHostTicksToTicks(long time)
{
return time * 19200;
}
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
{
lock (_waitingObjects)
{
_waitingObjects.RemoveAll(x => x.Object == Object);
}
}
private void WaitAndCheckScheduledObjects()
{
using (_waitEvent = new AutoResetEvent(false))
{
while (_keepRunning)
{
WaitingObject next;
lock (_waitingObjects)
{
next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
}
if (next != null)
{
long timePoint = PerformanceCounter.ElapsedMilliseconds;
if (next.TimePoint > timePoint)
{
_waitEvent.WaitOne((int)(next.TimePoint - timePoint));
}
bool timeUp = PerformanceCounter.ElapsedMilliseconds >= next.TimePoint;
if (timeUp)
{
lock (_waitingObjects)
{
timeUp = _waitingObjects.Remove(next);
}
}
if (timeUp)
{
next.Object.TimeUp();
}
}
else
{
_waitEvent.WaitOne();
}
}
}
return (long)((time / (double)PerformanceCounter.TicksPerSecond) * 19200000.0);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_keepRunning = false;
_waitEvent?.Set();
}
_keepRunning = false;
_waitEvent?.Set();
}
}
}

View file

@ -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))