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

@ -25,7 +25,7 @@ namespace Ryujinx.Configuration
public ReactiveObject<bool> DevColumn { get; private set; }
public ReactiveObject<bool> VersionColumn { get; private set; }
public ReactiveObject<bool> TimePlayedColumn { get; private set; }
public ReactiveObject<bool> LastPlayedColumn { get; private set; }
public ReactiveObject<bool> LastPlayedColumn { get; private set; }
public ReactiveObject<bool> FileExtColumn { get; private set; }
public ReactiveObject<bool> FileSizeColumn { get; private set; }
public ReactiveObject<bool> PathColumn { get; private set; }
@ -198,11 +198,6 @@ namespace Ryujinx.Configuration
/// </summary>
public ReactiveObject<bool> EnableDockedMode { get; private set; }
/// <summary>
/// Enables or disables multi-core scheduling of threads
/// </summary>
public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
/// <summary>
/// Enables or disables profiled translation cache persistency
/// </summary>
@ -230,17 +225,16 @@ namespace Ryujinx.Configuration
public SystemSection()
{
Language = new ReactiveObject<Language>();
Region = new ReactiveObject<Region>();
TimeZone = new ReactiveObject<string>();
SystemTimeOffset = new ReactiveObject<long>();
EnableDockedMode = new ReactiveObject<bool>();
EnableMulticoreScheduling = new ReactiveObject<bool>();
EnablePtc = new ReactiveObject<bool>();
EnableFsIntegrityChecks = new ReactiveObject<bool>();
FsGlobalAccessLogMode = new ReactiveObject<int>();
AudioBackend = new ReactiveObject<AudioBackend>();
IgnoreMissingServices = new ReactiveObject<bool>();
Language = new ReactiveObject<Language>();
Region = new ReactiveObject<Region>();
TimeZone = new ReactiveObject<string>();
SystemTimeOffset = new ReactiveObject<long>();
EnableDockedMode = new ReactiveObject<bool>();
EnablePtc = new ReactiveObject<bool>();
EnableFsIntegrityChecks = new ReactiveObject<bool>();
FsGlobalAccessLogMode = new ReactiveObject<int>();
AudioBackend = new ReactiveObject<AudioBackend>();
IgnoreMissingServices = new ReactiveObject<bool>();
}
}
@ -414,7 +408,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart = CheckUpdatesOnStart,
EnableVsync = Graphics.EnableVsync,
EnableShaderCache = Graphics.EnableShaderCache,
EnableMulticoreScheduling = System.EnableMulticoreScheduling,
EnablePtc = System.EnablePtc,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
@ -476,7 +469,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart.Value = true;
Graphics.EnableVsync.Value = true;
Graphics.EnableShaderCache.Value = true;
System.EnableMulticoreScheduling.Value = true;
System.EnablePtc.Value = false;
System.EnableFsIntegrityChecks.Value = true;
System.FsGlobalAccessLogMode.Value = 0;
@ -788,7 +780,6 @@ namespace Ryujinx.Configuration
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;

View file

@ -32,7 +32,7 @@ namespace Ryujinx.Common
public static long TicksPerMillisecond { get; }
/// <summary>
/// Gets the number of milliseconds elapsed since the system started.
/// Gets the number of ticks elapsed since the system started.
/// </summary>
public static long ElapsedTicks
{
@ -76,7 +76,7 @@ namespace Ryujinx.Common
TicksPerHour = TicksPerMinute * 60;
TicksPerDay = TicksPerHour * 24;
_ticksToNs = 1000000000.0 / (double)Stopwatch.Frequency;
_ticksToNs = 1000000000.0 / Stopwatch.Frequency;
}
}
}

View file

@ -82,9 +82,6 @@ namespace Ryujinx.HLE.HOS
public Keyset KeySet => Device.FileSystem.KeySet;
#pragma warning disable CS0649
private bool _hasStarted;
#pragma warning restore CS0649
private bool _isDisposed;
public bool EnablePtc { get; set; }
@ -300,22 +297,6 @@ namespace Ryujinx.HLE.HOS
VsyncEvent.ReadableEvent.Signal();
}
public void EnableMultiCoreScheduling()
{
if (!_hasStarted)
{
KernelContext.Scheduler.MultiCoreScheduling = true;
}
}
public void DisableMultiCoreScheduling()
{
if (!_hasStarted)
{
KernelContext.Scheduler.MultiCoreScheduling = false;
}
}
public void Dispose()
{
Dispose(true);
@ -346,9 +327,7 @@ namespace Ryujinx.HLE.HOS
}
// Exit ourself now!
KernelContext.Scheduler.ExitThread(terminationThread);
KernelContext.Scheduler.GetCurrentThread().Exit();
KernelContext.Scheduler.RemoveThread(terminationThread);
KernelStatic.GetCurrentThread().Exit();
});
terminationThread.Start();

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

View file

@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
clientSession = null;
KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
clientSession = null;
KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))

View file

@ -25,13 +25,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
State = ChannelState.Open;
CreatorProcess = context.Scheduler.GetCurrentProcess();
CreatorProcess = KernelStatic.GetCurrentProcess();
CreatorProcess.IncrementReferenceCount();
}
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize);
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);

View file

@ -214,7 +214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
KThread serverThread = KernelContext.Scheduler.GetCurrentThread();
KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner;
KernelContext.CriticalSection.Enter();
@ -594,7 +594,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
KThread serverThread = KernelContext.Scheduler.GetCurrentThread();
KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner;
KernelContext.CriticalSection.Enter();
@ -889,7 +889,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private MessageHeader GetServerMessageHeader(Message serverMsg)
{
KProcess currentProcess = KernelContext.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
uint word0 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 0);
uint word1 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 4);

View file

@ -1,6 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
@ -13,6 +12,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KSession(KernelContext context, KClientPort parentPort = null) : base(context)
{
IncrementReferenceCount();
ServerSession = new KServerSession(context, this);
ClientSession = new KClientSession(context, this, parentPort);

View file

@ -19,6 +19,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool KernelInitialized { get; }
public bool Running { get; private set; }
public Switch Device { get; }
public MemoryBlock Memory { get; }
public Syscall Syscall { get; }
@ -34,7 +36,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public KSlabHeap UserSlabHeapPages { get; }
public KCriticalSection CriticalSection { get; }
public KScheduler Scheduler { get; }
public KScheduler[] Schedulers { get; }
public KPriorityQueue PriorityQueue { get; }
public KTimeManager TimeManager { get; }
public KSynchronization Synchronization { get; }
public KContextIdManager ContextIdManager { get; }
@ -42,6 +45,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public ConcurrentDictionary<long, KProcess> Processes { get; }
public ConcurrentDictionary<string, KAutoObject> AutoObjectNames { get; }
public bool ThreadReselectionRequested { get; set; }
private long _kipId;
private long _processId;
private long _threadUid;
@ -51,6 +56,8 @@ namespace Ryujinx.HLE.HOS.Kernel
Device = device;
Memory = memory;
Running = true;
Syscall = new Syscall(this);
SyscallHandler = new SyscallHandler(this);
@ -70,12 +77,18 @@ namespace Ryujinx.HLE.HOS.Kernel
KernelConstants.UserSlabHeapSize);
CriticalSection = new KCriticalSection(this);
Scheduler = new KScheduler(this);
TimeManager = new KTimeManager();
Schedulers = new KScheduler[KScheduler.CpuCoresCount];
PriorityQueue = new KPriorityQueue();
TimeManager = new KTimeManager(this);
Synchronization = new KSynchronization(this);
ContextIdManager = new KContextIdManager();
Scheduler.StartAutoPreemptionThread();
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
Schedulers[core] = new KScheduler(this, core);
}
StartPreemptionThread();
KernelInitialized = true;
@ -86,6 +99,16 @@ namespace Ryujinx.HLE.HOS.Kernel
_processId = KernelConstants.InitialProcessId;
}
private void StartPreemptionThread()
{
void PreemptionThreadStart()
{
KScheduler.PreemptionThreadLoop(this);
}
new Thread(PreemptionThreadStart) { Name = "HLE.PreemptionThread" }.Start();
}
public long NewThreadUid()
{
return Interlocked.Increment(ref _threadUid) - 1;
@ -103,7 +126,13 @@ namespace Ryujinx.HLE.HOS.Kernel
public void Dispose()
{
Scheduler.Dispose();
Running = false;
for (int i = 0; i < KScheduler.CpuCoresCount; i++)
{
Schedulers[i].Dispose();
}
TimeManager.Dispose();
}
}

View file

@ -1,6 +1,9 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
using System.Threading.Tasks;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
@ -9,30 +12,52 @@ namespace Ryujinx.HLE.HOS.Kernel
[ThreadStatic]
private static KernelContext Context;
public static void YieldUntilCompletion(Action action)
[ThreadStatic]
private static KThread CurrentThread;
public static KernelResult StartInitialProcess(
KernelContext context,
ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities,
int mainThreadPriority,
ThreadStart customThreadStart)
{
YieldUntilCompletion(Task.Factory.StartNew(action));
}
KProcess process = new KProcess(context);
public static void YieldUntilCompletion(Task task)
{
KThread currentThread = Context.Scheduler.GetCurrentThread();
KernelResult result = process.Initialize(
creationInfo,
capabilities,
context.ResourceLimit,
MemoryRegion.Service,
null,
customThreadStart);
Context.CriticalSection.Enter();
currentThread.Reschedule(ThreadSchedState.Paused);
task.ContinueWith((antecedent) =>
if (result != KernelResult.Success)
{
currentThread.Reschedule(ThreadSchedState.Running);
});
return result;
}
Context.CriticalSection.Leave();
process.DefaultCpuCore = 3;
context.Processes.TryAdd(process.Pid, process);
return process.Start(mainThreadPriority, 0x1000UL);
}
internal static void SetKernelContext(KernelContext context)
internal static void SetKernelContext(KernelContext context, KThread thread)
{
Context = context;
CurrentThread = thread;
}
internal static KThread GetCurrentThread()
{
return CurrentThread;
}
internal static KProcess GetCurrentProcess()
{
return GetCurrentThread().Owner;
}
}
}

View file

@ -728,7 +728,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfMemory;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
lock (_blocks)
{
@ -1225,7 +1225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong remainingPages = remainingSize / PageSize;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null &&
!currentProcess.ResourceLimit.Reserve(LimitableResource.Memory, remainingSize))
@ -1355,7 +1355,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
PhysicalMemoryUsage -= heapMappedSize;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
currentProcess.ResourceLimit?.Release(LimitableResource.Memory, heapMappedSize);
@ -1504,7 +1504,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
attributeMask | MemoryAttribute.Uncached,
attributeExpected))
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
serverAddress = currentProcess.MemoryManager.GetDramAddressFromVa(serverAddress);
@ -2111,11 +2111,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
}
InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions);
return KernelResult.Success;
}
InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions);
return KernelResult.Success;
}
public KernelResult BorrowIpcBuffer(ulong address, ulong size)

View file

@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission)
{
KProcess creator = KernelContext.Scheduler.GetCurrentProcess();
KProcess creator = KernelStatic.GetCurrentProcess();
_creator = creator;

View file

@ -236,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
if (handle == SelfThreadHandle)
{
return _context.Scheduler.GetCurrentThread();
return KernelStatic.GetCurrentThread();
}
else
{
@ -248,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
if (handle == SelfProcessHandle)
{
return _context.Scheduler.GetCurrentProcess();
return KernelStatic.GetCurrentProcess();
}
else
{

View file

@ -78,6 +78,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public bool IsPaused { get; private set; }
private long _totalTimeRunning;
public long TotalTimeRunning => _totalTimeRunning;
private IProcessContextFactory _contextFactory;
public IProcessContext Context { get; private set; }
public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
@ -112,11 +116,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
KPageList pageList,
KResourceLimit resourceLimit,
MemoryRegion memRegion,
IProcessContextFactory contextFactory)
IProcessContextFactory contextFactory,
ThreadStart customThreadStart = null)
{
ResourceLimit = resourceLimit;
_memRegion = memRegion;
_contextFactory = contextFactory ?? new ProcessContextFactory();
_customThreadStart = customThreadStart;
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
@ -176,9 +182,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
}
result = ParseProcessInfo(creationInfo);
return result;
return ParseProcessInfo(creationInfo);
}
public KernelResult Initialize(
@ -192,6 +196,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ResourceLimit = resourceLimit;
_memRegion = memRegion;
_contextFactory = contextFactory ?? new ProcessContextFactory();
_customThreadStart = customThreadStart;
ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion);
@ -299,8 +304,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
CleanUpForError();
}
_customThreadStart = customThreadStart;
return result;
}
@ -751,8 +754,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private void InterruptHandler(object sender, EventArgs e)
{
KernelContext.Scheduler.ContextSwitch();
KernelContext.Scheduler.GetCurrentThread().HandlePostSyscall();
KThread currentThread = KernelStatic.GetCurrentThread();
if (currentThread.IsSchedulable)
{
KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
}
currentThread.HandlePostSyscall();
}
public void IncrementThreadCount()
@ -828,6 +837,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return personalMmHeapPagesCount * KMemoryManager.PageSize;
}
public void AddCpuTime(long ticks)
{
Interlocked.Add(ref _totalTimeRunning, ticks);
}
public void AddThread(KThread thread)
{
lock (_threadingLock)
@ -893,7 +907,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
if (shallTerminate)
{
UnpauseAndTerminateAllThreadsExcept(KernelContext.Scheduler.GetCurrentThread());
UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
HandleTable.Destroy();
@ -929,7 +943,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
if (shallTerminate)
{
UnpauseAndTerminateAllThreadsExcept(KernelContext.Scheduler.GetCurrentThread());
UnpauseAndTerminateAllThreadsExcept(KernelStatic.GetCurrentThread());
HandleTable.Destroy();
@ -1058,7 +1072,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private bool InvalidAccessHandler(ulong va)
{
KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace();
KernelStatic.GetCurrentThread().PrintGuestStackTrace();
Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}.");
@ -1067,7 +1081,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
{
KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace();
KernelStatic.GetCurrentThread().PrintGuestStackTrace();
throw new UndefinedInstructionException(e.Address, e.OpCode);
}

View file

@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetProcessId(int handle, out long pid)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidThread;
}
KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable;
KHandleTable handleTable = KernelStatic.GetCurrentProcess().HandleTable;
KProcess process = new KProcess(_context);
@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize)
{
KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
if (process == null)
{
@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.NotFound;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle);
@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SendSyncRequest(int handle)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
@ -303,7 +303,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
@ -363,7 +363,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
serverSessionHandle = 0;
clientSessionHandle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KResourceLimit resourceLimit = currentProcess.ResourceLimit;
@ -424,7 +424,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
sessionHandle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle);
@ -485,7 +485,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)((long)handlesCount * 4);
@ -513,7 +513,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
handleIndex = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
@ -582,7 +582,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)((long)handlesCount * 4);
@ -681,7 +681,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KPort port = new KPort(_context, maxSessions, isLight, (long)namePtr);
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
@ -733,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KPort port = new KPort(_context, maxSessions, false, 0);
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
@ -756,7 +756,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
clientSessionHandle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle);
@ -814,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.SetHeapSize(size, out position);
}
@ -843,7 +843,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.MemoryManager.SetMemoryAttribute(
position,
@ -871,7 +871,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{
@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.Map(dst, src, size);
}
@ -907,7 +907,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
{
@ -921,14 +921,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.Unmap(dst, src, size);
}
public KernelResult QueryMemory(ulong infoPtr, ulong position, out ulong pageInfo)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KMemoryInfo blkInfo = process.MemoryManager.QueryMemory(position);
@ -968,7 +968,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
@ -1009,7 +1009,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemState;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
@ -1056,7 +1056,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KResourceLimit resourceLimit = process.ResourceLimit;
@ -1112,7 +1112,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
@ -1125,7 +1125,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.MapPhysicalMemory(address, size);
}
@ -1147,7 +1147,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
@ -1160,7 +1160,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidMemRange;
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
return process.MemoryManager.UnmapPhysicalMemory(address, size);
}
@ -1177,7 +1177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@ -1214,7 +1214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@ -1259,7 +1259,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidPermission;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
@ -1285,7 +1285,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult TerminateProcess(int handle)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
process = process.HandleTable.GetObject<KProcess>(handle);
@ -1293,7 +1293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (process != null)
{
if (process == _context.Scheduler.GetCurrentProcess())
if (process == KernelStatic.GetCurrentProcess())
{
result = KernelResult.Success;
process.DecrementToZeroWhileTerminatingCurrent();
@ -1314,12 +1314,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void ExitProcess()
{
_context.Scheduler.GetCurrentProcess().TerminateCurrentProcess();
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
}
public KernelResult SignalEvent(int handle)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle);
@ -1343,7 +1343,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
KernelResult result;
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KWritableEvent writableEvent = process.HandleTable.GetObject<KWritableEvent>(handle);
@ -1363,14 +1363,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult CloseHandle(int handle)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
}
public KernelResult ResetSignal(int handle)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
@ -1399,12 +1399,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public ulong GetSystemTick()
{
return _context.Scheduler.GetCurrentThread().Context.CntpctEl0;
return KernelStatic.GetCurrentThread().Context.CntpctEl0;
}
public void Break(ulong reason)
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
if ((reason & (1UL << 31)) == 0)
{
@ -1429,7 +1429,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void OutputDebugString(ulong strPtr, ulong size)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
string str = MemoryHelper.ReadAsciiString(process.CpuMemory, (long)strPtr, (long)size);
@ -1466,7 +1466,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
@ -1537,7 +1537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
value = _context.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
value = KernelStatic.GetCurrentProcess().Debug ? 1 : 0;
break;
}
@ -1554,7 +1554,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.ResourceLimit != null)
{
@ -1581,14 +1581,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
int currentCore = _context.Scheduler.GetCurrentThread().CurrentCore;
int currentCore = KernelStatic.GetCurrentThread().CurrentCore;
if (subId != -1 && subId != currentCore)
{
return KernelResult.InvalidCombination;
}
value = _context.Scheduler.CoreContexts[currentCore].TotalIdleTimeTicks;
value = KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks);
break;
}
@ -1605,8 +1605,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
value = currentProcess.RandomEntropy[subId];
@ -1620,14 +1619,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidCombination;
}
KThread thread = _context.Scheduler.GetCurrentProcess().HandleTable.GetKThread(handle);
KThread thread = KernelStatic.GetCurrentProcess().HandleTable.GetKThread(handle);
if (thread == null)
{
return KernelResult.InvalidHandle;
}
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
int currentCore = currentThread.CurrentCore;
@ -1636,13 +1635,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
KCoreContext coreContext = _context.Scheduler.CoreContexts[currentCore];
KScheduler scheduler = _context.Schedulers[currentCore];
long timeDelta = PerformanceCounter.ElapsedMilliseconds - coreContext.LastContextSwitchTime;
long timeDelta = PerformanceCounter.ElapsedTicks - scheduler.LastContextSwitchTime;
if (subId != -1)
{
value = KTimeManager.ConvertMillisecondsToTicks(timeDelta);
value = KTimeManager.ConvertHostTicksToTicks(timeDelta);
}
else
{
@ -1653,7 +1652,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
totalTimeRunning += timeDelta;
}
value = KTimeManager.ConvertMillisecondsToTicks(totalTimeRunning);
value = KTimeManager.ConvertHostTicksToTicks(totalTimeRunning);
}
break;
@ -1669,7 +1668,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
KEvent Event = new KEvent(_context);
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle);
@ -1701,7 +1700,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (maxCount != 0)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
ulong copySize = (ulong)maxCount * 8;
@ -1807,7 +1806,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
handle = 0;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (cpuCore == -2)
{
@ -1844,7 +1843,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (result == KernelResult.Success)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
result = process.HandleTable.GenerateHandle(thread, out handle);
}
@ -1860,7 +1859,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult StartThread(int handle)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -1887,35 +1886,31 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public void ExitThread()
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
_context.Scheduler.ExitThread(currentThread);
KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.Exit();
}
public void SleepThread(long timeout)
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
if (timeout < 1)
{
switch (timeout)
{
case 0: currentThread.Yield(); break;
case -1: currentThread.YieldWithLoadBalancing(); break;
case -2: currentThread.YieldAndWaitForLoadBalancing(); break;
case 0: KScheduler.Yield(_context); break;
case -1: KScheduler.YieldWithLoadBalancing(_context); break;
case -2: KScheduler.YieldToAnyThread(_context); break;
}
}
else
{
currentThread.Sleep(timeout);
KernelStatic.GetCurrentThread().Sleep(timeout);
}
}
public KernelResult GetThreadPriority(int handle, out int priority)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -1937,7 +1932,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
// TODO: NPDM check.
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -1953,7 +1948,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetThreadCoreMask(int handle, out int preferredCore, out long affinityMask)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -1975,7 +1970,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (preferredCore == -2)
{
@ -2009,7 +2004,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -2023,12 +2018,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public int GetCurrentProcessorNumber()
{
return _context.Scheduler.GetCurrentThread().CurrentCore;
return KernelStatic.GetCurrentThread().CurrentCore;
}
public KernelResult GetThreadId(int handle, out long threadUid)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -2048,7 +2043,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SetThreadActivity(int handle, bool pause)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetObject<KThread>(handle);
@ -2062,7 +2057,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
if (thread == _context.Scheduler.GetCurrentThread())
if (thread == KernelStatic.GetCurrentThread())
{
return KernelResult.InvalidThread;
}
@ -2072,8 +2067,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult GetThreadContext3(ulong address, int handle)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KThread currentThread = _context.Scheduler.GetCurrentThread();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KThread currentThread = KernelStatic.GetCurrentThread();
KThread thread = currentProcess.HandleTable.GetObject<KThread>(handle);
@ -2190,13 +2185,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
var syncObjs = new Span<KSynchronizationObject>(currentThread.WaitSyncObjects).Slice(0, handlesCount);
if (handlesCount != 0)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr)
{
@ -2267,7 +2262,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult CancelSynchronization(int handle)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = process.HandleTable.GetKThread(handle);
@ -2293,7 +2288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
}
@ -2310,7 +2305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
}
@ -2331,7 +2326,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
mutexAddress,
@ -2342,7 +2337,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult SignalProcessWideKey(ulong address, int count)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
currentProcess.AddressArbiter.SignalProcessWideKey(address, count);
@ -2361,7 +2356,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
return type switch
{
@ -2387,7 +2382,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidAddress;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
return type switch
{

View file

@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private void PostSvcHandler()
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.HandlePostSyscall();
}

View file

@ -1,66 +0,0 @@
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class HleCoreManager
{
private class PausableThread
{
public ManualResetEvent Event { get; private set; }
public bool IsExiting { get; set; }
public PausableThread()
{
Event = new ManualResetEvent(false);
}
}
private ConcurrentDictionary<Thread, PausableThread> _threads;
public HleCoreManager()
{
_threads = new ConcurrentDictionary<Thread, PausableThread>();
}
public void Set(Thread thread)
{
GetThread(thread).Event.Set();
}
public void Reset(Thread thread)
{
GetThread(thread).Event.Reset();
}
public void Wait(Thread thread)
{
PausableThread pausableThread = GetThread(thread);
if (!pausableThread.IsExiting)
{
pausableThread.Event.WaitOne();
}
}
public void Exit(Thread thread)
{
GetThread(thread).IsExiting = true;
}
private PausableThread GetThread(Thread thread)
{
return _threads.GetOrAdd(thread, (key) => new PausableThread());
}
public void RemoveThread(Thread thread)
{
if (_threads.TryRemove(thread, out PausableThread pausableThread))
{
pausableThread.Event.Set();
pausableThread.Event.Dispose();
}
}
}
}

View file

@ -1,150 +0,0 @@
using System;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
partial class KScheduler
{
private const int RoundRobinTimeQuantumMs = 10;
private int _currentCore;
public bool MultiCoreScheduling { get; set; }
public HleCoreManager CoreManager { get; private set; }
private bool _keepPreempting;
public void StartAutoPreemptionThread()
{
Thread preemptionThread = new Thread(PreemptCurrentThread)
{
Name = "HLE.PreemptionThread"
};
_keepPreempting = true;
preemptionThread.Start();
}
public void ContextSwitch()
{
lock (CoreContexts)
{
if (MultiCoreScheduling)
{
int selectedCount = 0;
for (int core = 0; core < CpuCoresCount; core++)
{
KCoreContext coreContext = CoreContexts[core];
if (coreContext.ContextSwitchNeeded && (coreContext.CurrentThread?.IsCurrentHostThread() ?? false))
{
coreContext.ContextSwitch();
}
if (coreContext.CurrentThread?.IsCurrentHostThread() ?? false)
{
selectedCount++;
}
}
if (selectedCount == 0)
{
CoreManager.Reset(Thread.CurrentThread);
}
else if (selectedCount == 1)
{
CoreManager.Set(Thread.CurrentThread);
}
else
{
throw new InvalidOperationException("Thread scheduled in more than one core!");
}
}
else
{
KThread currentThread = CoreContexts[_currentCore].CurrentThread;
bool hasThreadExecuting = currentThread != null;
if (hasThreadExecuting)
{
// If this is not the thread that is currently executing, we need
// to request an interrupt to allow safely starting another thread.
if (!currentThread.IsCurrentHostThread())
{
currentThread.Context.RequestInterrupt();
return;
}
CoreManager.Reset(currentThread.HostThread);
}
// Advance current core and try picking a thread,
// keep advancing if it is null.
for (int core = 0; core < 4; core++)
{
_currentCore = (_currentCore + 1) % CpuCoresCount;
KCoreContext coreContext = CoreContexts[_currentCore];
coreContext.UpdateCurrentThread();
if (coreContext.CurrentThread != null)
{
CoreManager.Set(coreContext.CurrentThread.HostThread);
coreContext.CurrentThread.Execute();
break;
}
}
// If nothing was running before, then we are on a "external"
// HLE thread, we don't need to wait.
if (!hasThreadExecuting)
{
return;
}
}
}
CoreManager.Wait(Thread.CurrentThread);
}
private void PreemptCurrentThread()
{
// Preempts current thread every 10 milliseconds on a round-robin fashion,
// when multi core scheduling is disabled, to try ensuring that all threads
// gets a chance to run.
while (_keepPreempting)
{
lock (CoreContexts)
{
KThread currentThread = CoreContexts[_currentCore].CurrentThread;
currentThread?.Context.RequestInterrupt();
}
PreemptThreads();
Thread.Sleep(RoundRobinTimeQuantumMs);
}
}
public void ExitThread(KThread thread)
{
thread.Context.StopRunning();
CoreManager.Exit(thread.HostThread);
}
public void RemoveThread(KThread thread)
{
CoreManager.RemoveThread(thread.HostThread);
}
}
}

View file

@ -25,14 +25,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter();
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.Success;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!KernelTransfer.UserToKernelInt32(_context, mutexAddress, out int mutexValue))
{
@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
(KernelResult result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress);
@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.TimedOut;
@ -227,7 +227,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
ulong address = requester.MutexAddress;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address))
{
@ -293,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KernelResult WaitForAddressIfEqual(ulong address, int value, long timeout)
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter();
@ -368,7 +368,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
bool shouldDecrement,
long timeout)
{
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
_context.CriticalSection.Enter();
@ -383,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.TimedOut;
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!KernelTransfer.UserToKernelInt32(_context, address, out int currentValue))
{
@ -483,7 +483,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address))
{
@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
offset = 1;
}
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
KProcess currentProcess = KernelStatic.GetCurrentProcess();
if (!currentProcess.CpuMemory.IsMapped(address))
{

View file

@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
public static void Wait(KernelContext context, LinkedList<KThread> threadList, object mutex, long timeout)
{
KThread currentThread = context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();

View file

@ -1,79 +0,0 @@
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KCoreContext
{
private KScheduler _scheduler;
private HleCoreManager _coreManager;
public bool ContextSwitchNeeded { get; private set; }
public long LastContextSwitchTime { get; private set; }
public long TotalIdleTimeTicks { get; private set; } //TODO
public KThread CurrentThread { get; private set; }
public KThread SelectedThread { get; private set; }
public KCoreContext(KScheduler scheduler, HleCoreManager coreManager)
{
_scheduler = scheduler;
_coreManager = coreManager;
}
public void SelectThread(KThread thread)
{
SelectedThread = thread;
if (SelectedThread != CurrentThread)
{
ContextSwitchNeeded = true;
}
}
public void UpdateCurrentThread()
{
ContextSwitchNeeded = false;
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
CurrentThread = SelectedThread;
if (CurrentThread != null)
{
long currentTime = PerformanceCounter.ElapsedMilliseconds;
CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime;
CurrentThread.LastScheduledTime = currentTime;
}
}
public void ContextSwitch()
{
ContextSwitchNeeded = false;
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
if (CurrentThread != null)
{
_coreManager.Reset(CurrentThread.HostThread);
}
CurrentThread = SelectedThread;
if (CurrentThread != null)
{
long currentTime = PerformanceCounter.ElapsedMilliseconds;
CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime;
CurrentThread.LastScheduledTime = currentTime;
_coreManager.Set(CurrentThread.HostThread);
CurrentThread.Execute();
}
}
}
}

View file

@ -5,21 +5,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KCriticalSection
{
private readonly KernelContext _context;
public object LockObj { get; private set; }
private readonly object _lock;
private int _recursionCount;
public object Lock => _lock;
public KCriticalSection(KernelContext context)
{
_context = context;
LockObj = new object();
_lock = new object();
}
public void Enter()
{
Monitor.Enter(LockObj);
Monitor.Enter(_lock);
_recursionCount++;
}
@ -31,61 +30,34 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return;
}
bool doContextSwitch = false;
if (--_recursionCount == 0)
{
if (_context.Scheduler.ThreadReselectionRequested)
ulong scheduledCoresMask = KScheduler.SelectThreads(_context);
Monitor.Exit(_lock);
KThread currentThread = KernelStatic.GetCurrentThread();
bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable;
if (isCurrentThreadSchedulable)
{
_context.Scheduler.SelectThreads();
}
Monitor.Exit(LockObj);
if (_context.Scheduler.MultiCoreScheduling)
{
lock (_context.Scheduler.CoreContexts)
{
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
KCoreContext coreContext = _context.Scheduler.CoreContexts[core];
if (coreContext.ContextSwitchNeeded)
{
KThread currentThread = coreContext.CurrentThread;
if (currentThread == null)
{
// Nothing is running, we can perform the context switch immediately.
coreContext.ContextSwitch();
}
else if (currentThread.IsCurrentHostThread())
{
// Thread running on the current core, context switch will block.
doContextSwitch = true;
}
else
{
// Thread running on another core, request a interrupt.
currentThread.Context.RequestInterrupt();
}
}
}
}
KScheduler.EnableScheduling(_context, scheduledCoresMask);
}
else
{
doContextSwitch = true;
KScheduler.EnableSchedulingFromForeignThread(_context, scheduledCoresMask);
// If the thread exists but is not schedulable, we still want to suspend
// it if it's not runnable. That allows the kernel to still block HLE threads
// even if they are not scheduled on guest cores.
if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running)
{
currentThread.SchedulerWaitEvent.WaitOne();
}
}
}
else
{
Monitor.Exit(LockObj);
}
if (doContextSwitch)
{
_context.Scheduler.ContextSwitch();
Monitor.Exit(_lock);
}
}
}

View file

@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KSchedulingData
class KPriorityQueue
{
private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private long[] _scheduledPrioritiesPerCore;
private long[] _suggestedPrioritiesPerCore;
public KSchedulingData()
public KPriorityQueue()
{
_suggestedThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][];
_scheduledThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][];
@ -45,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
long prioMask = prios[core];
int prio = CountTrailingZeros(prioMask);
int prio = BitOperations.TrailingZeroCount(prioMask);
prioMask &= ~(1L << prio);
@ -62,42 +63,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
node = node.Next;
}
prio = CountTrailingZeros(prioMask);
prio = BitOperations.TrailingZeroCount(prioMask);
prioMask &= ~(1L << prio);
}
}
private int CountTrailingZeros(long value)
{
int count = 0;
while (((value >> count) & 0xf) == 0 && count < 64)
{
count += 4;
}
while (((value >> count) & 1) == 0 && count < 64)
{
count++;
}
return count;
}
public void TransferToCore(int prio, int dstCore, KThread thread)
{
bool schedulable = thread.DynamicPriority < KScheduler.PrioritiesCount;
int srcCore = thread.CurrentCore;
thread.CurrentCore = dstCore;
if (srcCore == dstCore || !schedulable)
int srcCore = thread.ActiveCore;
if (srcCore == dstCore)
{
return;
}
thread.ActiveCore = dstCore;
if (srcCore >= 0)
{
Unschedule(prio, srcCore, thread);
@ -168,13 +149,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_scheduledPrioritiesPerCore[core] |= 1L << prio;
}
public void Reschedule(int prio, int core, KThread thread)
public KThread Reschedule(int prio, int core, KThread thread)
{
if (prio >= KScheduler.PrioritiesCount)
{
return null;
}
LinkedList<KThread> queue = ScheduledQueue(prio, core);
queue.Remove(thread.SiblingsPerCore[core]);
thread.SiblingsPerCore[core] = queue.AddLast(thread);
return queue.First.Value;
}
public void Unschedule(int prio, int core, KThread thread)

View file

@ -1,7 +1,10 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
@ -10,130 +13,88 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public const int PrioritiesCount = 64;
public const int CpuCoresCount = 4;
private const int PreemptionPriorityCores012 = 59;
private const int PreemptionPriorityCore3 = 63;
private const int RoundRobinTimeQuantumMs = 10;
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
private readonly KernelContext _context;
private readonly int _coreId;
public KSchedulingData SchedulingData { get; private set; }
private struct SchedulingState
{
public bool NeedsScheduling;
public KThread SelectedThread;
}
public KCoreContext[] CoreContexts { get; private set; }
private SchedulingState _state;
public bool ThreadReselectionRequested { get; set; }
private AutoResetEvent _idleInterruptEvent;
private readonly object _idleInterruptEventLock;
public KScheduler(KernelContext context)
private KThread _previousThread;
private KThread _currentThread;
private readonly KThread _idleThread;
public KThread PreviousThread => _previousThread;
public long LastContextSwitchTime { get; private set; }
public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning;
public KScheduler(KernelContext context, int coreId)
{
_context = context;
_coreId = coreId;
SchedulingData = new KSchedulingData();
_idleInterruptEvent = new AutoResetEvent(false);
_idleInterruptEventLock = new object();
CoreManager = new HleCoreManager();
KThread idleThread = CreateIdleThread(context, coreId);
CoreContexts = new KCoreContext[CpuCoresCount];
_currentThread = idleThread;
_idleThread = idleThread;
idleThread.StartHostThread();
idleThread.SchedulerWaitEvent.Set();
}
private KThread CreateIdleThread(KernelContext context, int cpuCore)
{
KThread idleThread = new KThread(context);
idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop);
return idleThread;
}
public static ulong SelectThreads(KernelContext context)
{
if (context.ThreadReselectionRequested)
{
return SelectThreadsImpl(context);
}
else
{
return 0UL;
}
}
private static ulong SelectThreadsImpl(KernelContext context)
{
context.ThreadReselectionRequested = false;
ulong scheduledCoresMask = 0UL;
for (int core = 0; core < CpuCoresCount; core++)
{
CoreContexts[core] = new KCoreContext(this, CoreManager);
}
}
KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
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);
scheduledCoresMask |= context.Schedulers[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())
if (context.PriorityQueue.ScheduledThreads(core).Any())
{
continue;
}
@ -146,16 +107,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// 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))
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
if (thread.CurrentCore < 0 || thread != CoreContexts[thread.CurrentCore].SelectedThread)
if (suggested.ActiveCore < 0 || suggested != context.Schedulers[suggested.ActiveCore]._state.SelectedThread)
{
dst = thread;
dst = suggested;
break;
}
srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = thread.CurrentCore;
srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
}
// Not yet selected candidate found.
@ -165,9 +125,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// threads, we should skip load balancing entirely.
if (dst.DynamicPriority >= 2)
{
SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst);
CoreContexts[core].SelectThread(dst);
scheduledCoresMask |= context.Schedulers[core].SelectThread(dst);
}
continue;
@ -179,80 +139,480 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
int srcCore = srcCoresHighestPrioThreads[index];
KThread src = SchedulingData.ScheduledThreads(srcCore).ElementAtOrDefault(1);
KThread src = context.PriorityQueue.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;
KThread origSelectedCoreSrc = context.Schedulers[srcCore]._state.SelectedThread;
CoreContexts[srcCore].SelectThread(src);
scheduledCoresMask |= context.Schedulers[srcCore].SelectThread(src);
SchedulingData.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc);
context.PriorityQueue.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc);
CoreContexts[core].SelectThread(origSelectedCoreSrc);
scheduledCoresMask |= context.Schedulers[core].SelectThread(origSelectedCoreSrc);
}
}
}
return scheduledCoresMask;
}
public KThread GetCurrentThread()
private ulong SelectThread(KThread nextThread)
{
return GetCurrentThreadOrNull() ?? GetDummyThread();
}
KThread previousThread = _state.SelectedThread;
public KThread GetCurrentThreadOrNull()
{
lock (CoreContexts)
if (previousThread != nextThread)
{
if (previousThread != null)
{
previousThread.LastScheduledTime = PerformanceCounter.ElapsedTicks;
}
_state.SelectedThread = nextThread;
_state.NeedsScheduling = true;
return 1UL << _coreId;
}
else
{
return 0UL;
}
}
public static void EnableScheduling(KernelContext context, ulong scheduledCoresMask)
{
KScheduler currentScheduler = context.Schedulers[KernelStatic.GetCurrentThread().CurrentCore];
// Note that "RescheduleCurrentCore" will block, so "RescheduleOtherCores" must be done first.
currentScheduler.RescheduleOtherCores(scheduledCoresMask);
currentScheduler.RescheduleCurrentCore();
}
public static void EnableSchedulingFromForeignThread(KernelContext context, ulong scheduledCoresMask)
{
RescheduleOtherCores(context, scheduledCoresMask);
}
private void RescheduleCurrentCore()
{
if (_state.NeedsScheduling)
{
Schedule();
}
}
private void RescheduleOtherCores(ulong scheduledCoresMask)
{
RescheduleOtherCores(_context, scheduledCoresMask & ~(1UL << _coreId));
}
private static void RescheduleOtherCores(KernelContext context, ulong scheduledCoresMask)
{
while (scheduledCoresMask != 0)
{
int coreToSignal = BitOperations.TrailingZeroCount(scheduledCoresMask);
KThread threadToSignal = context.Schedulers[coreToSignal]._currentThread;
// Request the thread running on that core to stop and reschedule, if we have one.
if (threadToSignal != context.Schedulers[coreToSignal]._idleThread)
{
threadToSignal.Context.RequestInterrupt();
}
// If the core is idle, ensure that the idle thread is awaken.
context.Schedulers[coreToSignal]._idleInterruptEvent.Set();
scheduledCoresMask &= ~(1UL << coreToSignal);
}
}
private void IdleThreadLoop()
{
while (_context.Running)
{
_state.NeedsScheduling = false;
Thread.MemoryBarrier();
KThread nextThread = PickNextThread(_state.SelectedThread);
if (_idleThread != nextThread)
{
_idleThread.SchedulerWaitEvent.Reset();
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, _idleThread.SchedulerWaitEvent);
}
_idleInterruptEvent.WaitOne();
}
lock (_idleInterruptEventLock)
{
_idleInterruptEvent.Dispose();
_idleInterruptEvent = null;
}
}
public void Schedule()
{
_state.NeedsScheduling = false;
Thread.MemoryBarrier();
KThread currentThread = KernelStatic.GetCurrentThread();
KThread selectedThread = _state.SelectedThread;
// If the thread is already scheduled and running on the core, we have nothing to do.
if (currentThread == selectedThread)
{
return;
}
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();
}
KThread nextThread = PickNextThread(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);
}
else
{
// Allow the next thread to run.
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
// no longer assigned to a core.
currentThread.MakeUnschedulable();
// Just to be sure, set the core to a invalid value.
// This will trigger a exception if it attempts to call schedule again,
// rather than leaving the scheduler in a invalid state.
currentThread.CurrentCore = -1;
}
}
private KThread PickNextThread(KThread selectedThread)
{
while (true)
{
if (selectedThread != null)
{
// Try to run the selected thread.
// We need to acquire the context lock to be sure the thread is not
// already running on another core. If it is, then we return here
// and the caller should try again once there is something available for scheduling.
// The thread currently running on the core should have been requested to
// interrupt so this is not expected to take long.
// The idle thread must also be paused if we are scheduling a thread
// on the core, as the scheduled thread will handle the next switch.
if (selectedThread.ThreadContext.Lock())
{
SwitchTo(selectedThread);
if (!_state.NeedsScheduling)
{
return selectedThread;
}
selectedThread.ThreadContext.Unlock();
}
else
{
return _idleThread;
}
}
else
{
// The core is idle now, make sure that the idle thread can run
// and switch the core when a thread is available.
SwitchTo(null);
return _idleThread;
}
_state.NeedsScheduling = false;
Thread.MemoryBarrier();
selectedThread = _state.SelectedThread;
}
}
private void SwitchTo(KThread nextThread)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KThread currentThread = KernelStatic.GetCurrentThread();
nextThread ??= _idleThread;
if (currentThread == nextThread)
{
return;
}
long previousTicks = LastContextSwitchTime;
long currentTicks = PerformanceCounter.ElapsedTicks;
long ticksDelta = currentTicks - previousTicks;
currentThread.AddCpuTime(ticksDelta);
if (currentProcess != null)
{
currentProcess.AddCpuTime(ticksDelta);
}
LastContextSwitchTime = currentTicks;
if (currentProcess != null)
{
_previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null;
}
else if (currentThread == _idleThread)
{
_previousThread = null;
}
if (nextThread.CurrentCore != _coreId)
{
nextThread.CurrentCore = _coreId;
}
_currentThread = nextThread;
}
public static void PreemptionThreadLoop(KernelContext context)
{
while (context.Running)
{
context.CriticalSection.Enter();
for (int core = 0; core < CpuCoresCount; core++)
{
if (CoreContexts[core].CurrentThread?.IsCurrentHostThread() ?? false)
RotateScheduledQueue(context, core, PreemptionPriorities[core]);
}
context.CriticalSection.Leave();
Thread.Sleep(RoundRobinTimeQuantumMs);
}
}
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
{
IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
KThread nextThread = null;
// Yield priority queue.
if (selectedThread != null)
{
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
}
IEnumerable<KThread> SuitableCandidates()
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore >= 0)
{
return CoreContexts[core].CurrentThread;
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
{
continue;
}
}
// If the candidate was scheduled after the current thread, then it's not worth it.
if (nextThread == selectedThread ||
nextThread == null ||
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
{
yield return suggested;
}
}
}
return null;
}
// 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);
private KThread _dummyThread;
private KThread GetDummyThread()
{
if (_dummyThread != null)
if (dst != null)
{
return _dummyThread;
context.PriorityQueue.TransferToCore(prio, core, dst);
}
KProcess dummyProcess = new KProcess(_context);
// If the priority of the currently selected thread is lower or same as the preemption priority,
// then try to migrate a thread with lower priority.
KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
dummyProcess.HandleTable.Initialize(1024);
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
{
dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
KThread dummyThread = new KThread(_context);
if (dst != null)
{
context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst);
}
}
dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
return _dummyThread = dummyThread;
context.ThreadReselectionRequested = true;
}
public KProcess GetCurrentProcess()
public static void Yield(KernelContext context)
{
return GetCurrentThread().Owner;
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
{
context.CriticalSection.Leave();
return;
}
KThread nextThread = context.PriorityQueue.Reschedule(currentThread.DynamicPriority, currentThread.ActiveCore, currentThread);
if (nextThread != currentThread)
{
context.ThreadReselectionRequested = true;
}
context.CriticalSection.Leave();
}
public static void YieldWithLoadBalancing(KernelContext context)
{
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
{
context.CriticalSection.Leave();
return;
}
int prio = currentThread.DynamicPriority;
int core = currentThread.ActiveCore;
// Move current thread to the end of the queue.
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
IEnumerable<KThread> SuitableCandidates()
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore >= 0)
{
KThread selectedSuggestedCore = context.Schedulers[suggestedCore]._state.SelectedThread;
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 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 (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
suggested.DynamicPriority < nextThread.DynamicPriority)
{
yield return suggested;
}
}
}
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
if (dst != null)
{
context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst);
context.ThreadReselectionRequested = true;
}
else if (currentThread != nextThread)
{
context.ThreadReselectionRequested = true;
}
context.CriticalSection.Leave();
}
public static void YieldToAnyThread(KernelContext context)
{
KThread currentThread = KernelStatic.GetCurrentThread();
context.CriticalSection.Enter();
if (currentThread.SchedFlags != ThreadSchedState.Running)
{
context.CriticalSection.Leave();
return;
}
int core = currentThread.ActiveCore;
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
if (!context.PriorityQueue.ScheduledThreads(core).Any())
{
KThread selectedThread = null;
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore < 0)
{
continue;
}
KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
if (firstCandidate == suggested)
{
continue;
}
if (firstCandidate == null || firstCandidate.DynamicPriority >= 2)
{
context.PriorityQueue.TransferToCore(suggested.DynamicPriority, core, suggested);
}
selectedThread = suggested;
break;
}
if (currentThread != selectedThread)
{
context.ThreadReselectionRequested = true;
}
}
else
{
context.ThreadReselectionRequested = true;
}
context.CriticalSection.Leave();
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
// Ensure that the idle thread is not blocked and can exit.
lock (_idleInterruptEventLock)
{
_keepPreempting = false;
if (_idleInterruptEvent != null)
{
_idleInterruptEvent.Set();
}
}
}
}

View file

@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return result;
}
KThread currentThread = _context.Scheduler.GetCurrentThread();
KThread currentThread = KernelStatic.GetCurrentThread();
if (currentThread.ShallBeTerminated ||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)

View file

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

View file

@ -0,0 +1,19 @@
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KThreadContext
{
private int _locked;
public bool Lock()
{
return Interlocked.Exchange(ref _locked, 1) == 0;
}
public void Unlock()
{
Interlocked.Exchange(ref _locked, 0);
}
}
}

View file

@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services
};
private readonly KernelContext _context;
private readonly KProcess _selfProcess;
private KProcess _selfProcess;
private readonly List<int> _sessionHandles = new List<int>();
private readonly List<int> _portHandles = new List<int>();
@ -55,11 +55,7 @@ namespace Ryujinx.HLE.HOS.Services
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
context.Syscall.CreateProcess(creationInfo, DefaultCapabilities, out int handle, null, ServerLoop);
_selfProcess = context.Scheduler.GetCurrentProcess().HandleTable.GetKProcess(handle);
context.Syscall.StartProcess(handle, 44, 3, 0x1000);
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
}
private void AddPort(int serverPortHandle, IpcService obj)
@ -82,6 +78,8 @@ namespace Ryujinx.HLE.HOS.Services
private void ServerLoop()
{
_selfProcess = KernelStatic.GetCurrentProcess();
if (SmObject != null)
{
_context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
@ -95,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services
InitDone.Dispose();
}
KThread thread = _context.Scheduler.GetCurrentThread();
KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress;
_context.Syscall.SetHeapSize(0x200000, out ulong heapAddr);
@ -107,18 +105,14 @@ namespace Ryujinx.HLE.HOS.Services
while (true)
{
int[] handles = _portHandles.ToArray();
int[] portHandles = _portHandles.ToArray();
int[] sessionHandles = _sessionHandles.ToArray();
int[] handles = new int[portHandles.Length + sessionHandles.Length];
for (int i = 0; i < handles.Length; i++)
{
if (_context.Syscall.AcceptSession(handles[i], out int serverSessionHandle) == KernelResult.Success)
{
AddSessionObj(serverSessionHandle, _ports[handles[i]]);
}
}
handles = _sessionHandles.ToArray();
portHandles.CopyTo(handles, 0);
sessionHandles.CopyTo(handles, portHandles.Length);
// We still need a timeout here to allow the service to pick up and listen new sessions...
var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex);
thread.HandlePostSyscall();
@ -130,8 +124,9 @@ namespace Ryujinx.HLE.HOS.Services
replyTargetHandle = 0;
if (rc == KernelResult.Success && signaledIndex != -1)
if (rc == KernelResult.Success && signaledIndex >= portHandles.Length)
{
// We got a IPC request, process it, pass to the appropriate service if needed.
int signaledHandle = handles[signaledIndex];
if (Process(signaledHandle, heapAddr))
@ -141,6 +136,15 @@ namespace Ryujinx.HLE.HOS.Services
}
else
{
if (rc == KernelResult.Success)
{
// We got a new connection, accept the session to allow servicing future requests.
if (_context.Syscall.AcceptSession(handles[signaledIndex], out int serverSessionHandle) == KernelResult.Success)
{
AddSessionObj(serverSessionHandle, _ports[handles[signaledIndex]]);
}
}
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
@ -150,8 +154,8 @@ namespace Ryujinx.HLE.HOS.Services
private bool Process(int serverSessionHandle, ulong recvListAddr)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
KThread thread = _context.Scheduler.GetCurrentThread();
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress;
ulong messageSize = 0x100;

View file

@ -184,11 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public void WaitDequeueEvent()
{
Monitor.Exit(Lock);
KernelStatic.YieldUntilCompletion(WaitForLock);
Monitor.Enter(Lock);
WaitForLock();
}
public void SignalIsAllocatingEvent()
@ -198,21 +194,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public void WaitIsAllocatingEvent()
{
Monitor.Exit(Lock);
KernelStatic.YieldUntilCompletion(WaitForLock);
Monitor.Enter(Lock);
WaitForLock();
}
private void WaitForLock()
{
lock (Lock)
if (Active)
{
if (Active)
{
Monitor.Wait(Lock);
}
Monitor.Wait(Lock);
}
}

View file

@ -115,11 +115,6 @@ namespace Ryujinx.HLE
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
{
System.EnableMultiCoreScheduling();
}
System.EnablePtc = ConfigurationState.Instance.System.EnablePtc;
System.FsIntegrityCheckLevel = GetIntegrityCheckLevel();

View file

@ -1,4 +1,4 @@
{
{
"version": 17,
"res_scale": 1,
"res_scale_custom": 1,
@ -22,7 +22,6 @@
"check_updates_on_start": true,
"enable_vsync": true,
"enable_shader_cache": true,
"enable_multicore_scheduling": true,
"enable_ptc": false,
"enable_fs_integrity_checks": true,
"fs_global_access_log_mode": 0,

View file

@ -200,4 +200,29 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
# Atmosphère (MIT)
```
MIT License
Copyright (c) 2018-2020 Atmosphère-NX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View file

@ -43,7 +43,6 @@ namespace Ryujinx.Ui
[GUI] CheckButton _checkUpdatesToggle;
[GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _shaderCacheToggle;
[GUI] CheckButton _multiSchedToggle;
[GUI] CheckButton _ptcToggle;
[GUI] CheckButton _fsicToggle;
[GUI] CheckButton _ignoreToggle;
@ -188,11 +187,6 @@ namespace Ryujinx.Ui
_shaderCacheToggle.Click();
}
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
{
_multiSchedToggle.Click();
}
if (ConfigurationState.Instance.System.EnablePtc)
{
_ptcToggle.Click();
@ -401,7 +395,6 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active;
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active;
ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active;
ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
@ -490,7 +483,7 @@ namespace Ryujinx.Ui
foreach (string directory in fileChooser.Filenames)
{
bool directoryAdded = false;
if (_gameDirsBoxStore.GetIterFirst(out TreeIter treeIter))
{
do

View file

@ -1439,24 +1439,6 @@
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="_multiSchedToggle">
<property name="label" translatable="yes">Enable Multicore Scheduling</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="tooltip-text" translatable="yes">Enables or disables multi-core scheduling of threads</property>
<property name="halign">start</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="_ptcToggle">
<property name="label" translatable="yes">Enable Profiled Persistent Translation Cache</property>

View file

@ -18,7 +18,6 @@
"system_region",
"docked_mode",
"enable_vsync",
"enable_multicore_scheduling",
"enable_ptc",
"enable_fs_integrity_checks",
"fs_global_access_log_mode",
@ -1196,17 +1195,6 @@
false
]
},
"enable_multicore_scheduling": {
"$id": "#/properties/enable_multicore_scheduling",
"type": "boolean",
"title": "Enable Multicore Scheduling",
"description": "Enables or disables multi-core scheduling of threads",
"default": true,
"examples": [
true,
false
]
},
"enable_ptc": {
"$id": "#/properties/enable_ptc",
"type": "boolean",