Initial work to support changing thread core on the scheduler, also some cond var priority fixes

This commit is contained in:
gdkchan 2018-05-13 00:22:42 -03:00
parent 3603497a13
commit 4546d1b9be
9 changed files with 442 additions and 199 deletions

View File

@ -1,130 +1,16 @@
using Ryujinx.Core.Logging; using Ryujinx.Core.Logging;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Core.OsHle.Handles namespace Ryujinx.Core.OsHle.Handles
{ {
class KProcessScheduler : IDisposable class KProcessScheduler : IDisposable
{ {
private const int LowestPriority = 0x40;
private class SchedulerThread : IDisposable
{
public KThread Thread { get; private set; }
public bool IsActive { get; set; }
public AutoResetEvent WaitSync { get; private set; }
public ManualResetEvent WaitActivity { get; private set; }
public AutoResetEvent WaitSched { get; private set; }
public SchedulerThread(KThread Thread)
{
this.Thread = Thread;
IsActive = true;
WaitSync = new AutoResetEvent(false);
WaitActivity = new ManualResetEvent(true);
WaitSched = new AutoResetEvent(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
WaitSync.Dispose();
WaitActivity.Dispose();
WaitSched.Dispose();
}
}
}
private class ThreadQueue
{
private List<SchedulerThread> Threads;
public ThreadQueue()
{
Threads = new List<SchedulerThread>();
}
public void Push(SchedulerThread Thread)
{
lock (Threads)
{
Threads.Add(Thread);
}
}
public SchedulerThread Pop(int MinPriority = LowestPriority)
{
lock (Threads)
{
SchedulerThread SchedThread;
int HighestPriority = MinPriority;
int HighestPrioIndex = -1;
for (int Index = 0; Index < Threads.Count; Index++)
{
SchedThread = Threads[Index];
if (HighestPriority > SchedThread.Thread.ActualPriority)
{
HighestPriority = SchedThread.Thread.ActualPriority;
HighestPrioIndex = Index;
}
}
if (HighestPrioIndex == -1)
{
return null;
}
SchedThread = Threads[HighestPrioIndex];
Threads.RemoveAt(HighestPrioIndex);
return SchedThread;
}
}
public bool HasThread(SchedulerThread SchedThread)
{
lock (Threads)
{
return Threads.Contains(SchedThread);
}
}
public bool Remove(SchedulerThread SchedThread)
{
lock (Threads)
{
return Threads.Remove(SchedThread);
}
}
}
private ConcurrentDictionary<KThread, SchedulerThread> AllThreads; private ConcurrentDictionary<KThread, SchedulerThread> AllThreads;
private ThreadQueue[] WaitingToRun; private ThreadQueue WaitingToRun;
private HashSet<int> ActiveProcessors; private int ActiveCores;
private object SchedLock; private object SchedLock;
@ -136,14 +22,7 @@ namespace Ryujinx.Core.OsHle.Handles
AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>(); AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>();
WaitingToRun = new ThreadQueue[4]; WaitingToRun = new ThreadQueue();
for (int Index = 0; Index < 4; Index++)
{
WaitingToRun[Index] = new ThreadQueue();
}
ActiveProcessors = new HashSet<int>();
SchedLock = new object(); SchedLock = new object();
} }
@ -159,7 +38,7 @@ namespace Ryujinx.Core.OsHle.Handles
return; return;
} }
if (ActiveProcessors.Add(Thread.ProcessorId)) if (AddActiveCore(Thread))
{ {
Thread.Thread.Execute(); Thread.Thread.Execute();
@ -167,7 +46,7 @@ namespace Ryujinx.Core.OsHle.Handles
} }
else else
{ {
WaitingToRun[Thread.ProcessorId].Push(SchedThread); WaitingToRun.Push(SchedThread);
PrintDbgThreadInfo(Thread, "waiting to run."); PrintDbgThreadInfo(Thread, "waiting to run.");
} }
@ -182,18 +61,18 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread)) if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread))
{ {
WaitingToRun[Thread.ProcessorId].Remove(SchedThread); WaitingToRun.Remove(SchedThread);
SchedThread.Dispose(); SchedThread.Dispose();
} }
SchedulerThread NewThread = WaitingToRun[Thread.ProcessorId].Pop(); SchedulerThread NewThread = WaitingToRun.Pop(Thread.ActualCore);
if (NewThread == null) if (NewThread == null)
{ {
Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ProcessorId}!"); Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!");
ActiveProcessors.Remove(Thread.ProcessorId); RemoveActiveCore(Thread.ActualCore);
return; return;
} }
@ -228,7 +107,7 @@ namespace Ryujinx.Core.OsHle.Handles
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
Suspend(Thread.ProcessorId); Suspend(Thread);
SchedThread.WaitSync.WaitOne(); SchedThread.WaitSync.WaitOne();
@ -242,7 +121,7 @@ namespace Ryujinx.Core.OsHle.Handles
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
Suspend(Thread.ProcessorId); Suspend(Thread);
bool Result = SchedThread.WaitSync.WaitOne(Timeout); bool Result = SchedThread.WaitSync.WaitOne(Timeout);
@ -261,11 +140,13 @@ namespace Ryujinx.Core.OsHle.Handles
SchedThread.WaitSync.Set(); SchedThread.WaitSync.Set();
} }
public void Suspend(int ProcessorId) public void Suspend(KThread Thread)
{ {
lock (SchedLock) lock (SchedLock)
{ {
SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop(); PrintDbgThreadInfo(Thread, "suspended.");
SchedulerThread SchedThread = WaitingToRun.Pop(Thread.ActualCore);
if (SchedThread != null) if (SchedThread != null)
{ {
@ -273,9 +154,9 @@ namespace Ryujinx.Core.OsHle.Handles
} }
else else
{ {
Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ProcessorId}!"); Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!");
ActiveProcessors.Remove(ProcessorId); RemoveActiveCore(Thread.ActualCore);
} }
} }
} }
@ -288,7 +169,9 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
lock (SchedLock) lock (SchedLock)
{ {
SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority); SchedulerThread SchedThread = WaitingToRun.Pop(
Thread.ActualCore,
Thread.ActualPriority);
if (SchedThread == null) if (SchedThread == null)
{ {
@ -307,7 +190,7 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
//Just stop running the thread if it's not active, //Just stop running the thread if it's not active,
//and run whatever is waiting to run with the higuest priority. //and run whatever is waiting to run with the higuest priority.
Suspend(Thread.ProcessorId); Suspend(Thread);
} }
Resume(Thread); Resume(Thread);
@ -333,14 +216,14 @@ namespace Ryujinx.Core.OsHle.Handles
lock (SchedLock) lock (SchedLock)
{ {
if (ActiveProcessors.Add(Thread.ProcessorId)) if (AddActiveCore(Thread))
{ {
PrintDbgThreadInfo(Thread, "resuming execution..."); PrintDbgThreadInfo(Thread, "resuming execution...");
return; return;
} }
WaitingToRun[Thread.ProcessorId].Push(SchedThread); WaitingToRun.Push(SchedThread);
PrintDbgThreadInfo(Thread, "entering wait state..."); PrintDbgThreadInfo(Thread, "entering wait state...");
} }
@ -354,6 +237,8 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
if (!SchedThread.Thread.Thread.Execute()) if (!SchedThread.Thread.Thread.Execute())
{ {
PrintDbgThreadInfo(SchedThread.Thread, "waked.");
SchedThread.WaitSched.Set(); SchedThread.WaitSched.Set();
} }
else else
@ -362,6 +247,14 @@ namespace Ryujinx.Core.OsHle.Handles
} }
} }
public void Resort(KThread Thread)
{
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
WaitingToRun.Resort(SchedThread);
}
}
private bool IsActive(KThread Thread) private bool IsActive(KThread Thread)
{ {
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
@ -372,11 +265,62 @@ namespace Ryujinx.Core.OsHle.Handles
return SchedThread.IsActive; return SchedThread.IsActive;
} }
private bool AddActiveCore(KThread Thread)
{
lock (SchedLock)
{
//First, try running it on Ideal Core.
int CoreMask = 1 << Thread.IdealCore;
if ((ActiveCores & CoreMask) == 0)
{
ActiveCores |= CoreMask;
Thread.ActualCore = Thread.IdealCore;
return true;
}
//If that fails, then try running on any core allowed by Core Mask.
CoreMask = Thread.CoreMask & ~ActiveCores;
if (CoreMask != 0)
{
CoreMask &= -CoreMask;
ActiveCores |= CoreMask;
for (int Bit = 0; Bit < 32; Bit++)
{
if (((CoreMask >> Bit) & 1) != 0)
{
Thread.ActualCore = Bit;
return true;
}
}
throw new InvalidOperationException();
}
return false;
}
}
private void RemoveActiveCore(int Core)
{
lock (SchedLock)
{
ActiveCores &= ~(1 << Core);
}
}
private void PrintDbgThreadInfo(KThread Thread, string Message) private void PrintDbgThreadInfo(KThread Thread, string Message)
{ {
Log.PrintDebug(LogClass.KernelScheduler, "(" + Log.PrintDebug(LogClass.KernelScheduler, "(" +
"ThreadId = " + Thread.ThreadId + ", " + "ThreadId = " + Thread.ThreadId + ", " +
"ProcessorId = " + Thread.ProcessorId + ", " + "ActualCore = " + Thread.ActualCore + ", " +
"IdealCore = " + Thread.IdealCore + ", " +
"ActualPriority = " + Thread.ActualPriority + ", " + "ActualPriority = " + Thread.ActualPriority + ", " +
"WantedPriority = " + Thread.WantedPriority + ") " + Message); "WantedPriority = " + Thread.WantedPriority + ") " + Message);
} }

View File

@ -7,9 +7,13 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
public AThread Thread { get; private set; } public AThread Thread { get; private set; }
public int CoreMask { get; set; }
public long MutexAddress { get; set; } public long MutexAddress { get; set; }
public long CondVarAddress { get; set; } public long CondVarAddress { get; set; }
private Process Process;
public KThread NextMutexThread { get; set; } public KThread NextMutexThread { get; set; }
public KThread NextCondVarThread { get; set; } public KThread NextCondVarThread { get; set; }
@ -18,16 +22,24 @@ namespace Ryujinx.Core.OsHle.Handles
public int ActualPriority { get; private set; } public int ActualPriority { get; private set; }
public int WantedPriority { get; private set; } public int WantedPriority { get; private set; }
public int ProcessorId { get; private set; } public int IdealCore { get; private set; }
public int ActualCore { get; set; }
public int WaitHandle { get; set; } public int WaitHandle { get; set; }
public int ThreadId => Thread.ThreadId; public int ThreadId => Thread.ThreadId;
public KThread(AThread Thread, int ProcessorId, int Priority) public KThread(
AThread Thread,
Process Process,
int IdealCore,
int Priority)
{ {
this.Thread = Thread; this.Thread = Thread;
this.ProcessorId = ProcessorId; this.Process = Process;
this.IdealCore = IdealCore;
CoreMask = 1 << IdealCore;
ActualPriority = WantedPriority = Priority; ActualPriority = WantedPriority = Priority;
} }
@ -54,19 +66,30 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
ActualPriority = CurrPriority; ActualPriority = CurrPriority;
UpdateWaitList(); UpdateWaitLists();
MutexOwner?.UpdatePriority(); MutexOwner?.UpdatePriority();
} }
} }
private void UpdateWaitList() private void UpdateWaitLists()
{
UpdateMutexList();
UpdateCondVarList();
Process.Scheduler.Resort(this);
}
private void UpdateMutexList()
{ {
KThread OwnerThread = MutexOwner; KThread OwnerThread = MutexOwner;
if (OwnerThread != null) if (OwnerThread == null)
{ {
//The MutexOwner field should only be non null when the thread is return;
}
//The MutexOwner field should only be non-null when the thread is
//waiting for the lock, and the lock belongs to another thread. //waiting for the lock, and the lock belongs to another thread.
if (OwnerThread == this) if (OwnerThread == this)
{ {
@ -95,7 +118,7 @@ namespace Ryujinx.Core.OsHle.Handles
while (CurrThread.NextMutexThread != null) while (CurrThread.NextMutexThread != null)
{ {
if (CurrThread.NextMutexThread.ActualPriority < ActualPriority) if (CurrThread.NextMutexThread.ActualPriority > ActualPriority)
{ {
break; break;
} }
@ -108,6 +131,74 @@ namespace Ryujinx.Core.OsHle.Handles
CurrThread.NextMutexThread = this; CurrThread.NextMutexThread = this;
} }
} }
private void UpdateCondVarList()
{
lock (Process.ThreadArbiterListLock)
{
if (Process.ThreadArbiterListHead == null)
{
return;
}
//Remove itself from the list.
bool Found;
KThread CurrThread = Process.ThreadArbiterListHead;
if (Found = (Process.ThreadArbiterListHead == this))
{
Process.ThreadArbiterListHead = Process.ThreadArbiterListHead.NextCondVarThread;
}
else
{
while (CurrThread.NextCondVarThread != null)
{
if (CurrThread.NextCondVarThread == this)
{
CurrThread.NextCondVarThread = NextCondVarThread;
Found = true;
break;
}
CurrThread = CurrThread.NextCondVarThread;
}
}
if (!Found)
{
return;
}
//Re-add taking new priority into account.
if (Process.ThreadArbiterListHead == null ||
Process.ThreadArbiterListHead.ActualPriority > ActualPriority)
{
NextCondVarThread = Process.ThreadArbiterListHead;
Process.ThreadArbiterListHead = this;
return;
}
CurrThread = Process.ThreadArbiterListHead;
while (CurrThread.NextCondVarThread != null)
{
if (CurrThread.NextCondVarThread.ActualPriority > ActualPriority)
{
break;
}
CurrThread = CurrThread.NextCondVarThread;
}
NextCondVarThread = CurrThread.NextCondVarThread;
CurrThread.NextCondVarThread = this;
}
} }
} }
} }

View File

@ -0,0 +1,48 @@
using System;
using System.Threading;
namespace Ryujinx.Core.OsHle.Handles
{
class SchedulerThread : IDisposable
{
public KThread Thread { get; private set; }
public SchedulerThread Next { get; set; }
public bool IsActive { get; set; }
public AutoResetEvent WaitSync { get; private set; }
public ManualResetEvent WaitActivity { get; private set; }
public AutoResetEvent WaitSched { get; private set; }
public SchedulerThread(KThread Thread)
{
this.Thread = Thread;
IsActive = true;
WaitSync = new AutoResetEvent(false);
WaitActivity = new ManualResetEvent(true);
WaitSched = new AutoResetEvent(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
WaitSync.Dispose();
WaitActivity.Dispose();
WaitSched.Dispose();
}
}
}
}

View File

@ -0,0 +1,158 @@
namespace Ryujinx.Core.OsHle.Handles
{
class ThreadQueue
{
private const int LowestPriority = 0x40;
private SchedulerThread Head;
private object ListLock;
public ThreadQueue()
{
ListLock = new object();
}
public void Push(SchedulerThread Wait)
{
lock (ListLock)
{
//Ensure that we're not creating circular references
//by adding a thread that is already on the list.
if (HasThread(Wait))
{
return;
}
if (Head == null || Head.Thread.ActualPriority > Wait.Thread.ActualPriority)
{
Wait.Next = Head;
Head = Wait;
return;
}
SchedulerThread Curr = Head;
while (Curr.Next != null)
{
if (Curr.Next.Thread.ActualPriority > Wait.Thread.ActualPriority)
{
break;
}
Curr = Curr.Next;
}
Wait.Next = Curr.Next;
Curr.Next = Wait;
}
}
public SchedulerThread Pop(int Core, int MinPriority = LowestPriority)
{
lock (ListLock)
{
int CoreMask = 1 << Core;
SchedulerThread Prev = null;
SchedulerThread Curr = Head;
while (Curr != null)
{
KThread Thread = Curr.Thread;
if (Thread.ActualPriority <= MinPriority && (Thread.CoreMask & CoreMask) != 0)
{
if (Prev != null)
{
Prev.Next = Curr.Next;
}
else
{
Head = Head.Next;
}
break;
}
Prev = Curr;
Curr = Curr.Next;
}
return Curr;
}
}
public bool Remove(SchedulerThread Thread)
{
lock (ListLock)
{
if (Head == null)
{
return false;
}
else if (Head == Thread)
{
Head = Head.Next;
return true;
}
SchedulerThread Prev = Head;
SchedulerThread Curr = Head.Next;
while (Curr != null)
{
if (Curr == Thread)
{
Prev.Next = Curr.Next;
return true;
}
Prev = Curr;
Curr = Curr.Next;
}
return false;
}
}
public bool Resort(SchedulerThread Thread)
{
lock (ListLock)
{
if (Remove(Thread))
{
Push(Thread);
return true;
}
return false;
}
}
public bool HasThread(SchedulerThread Thread)
{
lock (ListLock)
{
SchedulerThread Curr = Head;
while (Curr != null)
{
if (Curr == Thread)
{
return true;
}
Curr = Curr.Next;
}
return false;
}
}
}
}

View File

@ -22,8 +22,6 @@ namespace Ryujinx.Core.OsHle.Kernel
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits; private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
private object CondVarLock;
private HashSet<(HSharedMem, long)> MappedSharedMems; private HashSet<(HSharedMem, long)> MappedSharedMems;
private ulong CurrentHeapSize; private ulong CurrentHeapSize;
@ -80,8 +78,6 @@ namespace Ryujinx.Core.OsHle.Kernel
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>(); SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
CondVarLock = new object();
MappedSharedMems = new HashSet<(HSharedMem, long)>(); MappedSharedMems = new HashSet<(HSharedMem, long)>();
} }

View File

@ -131,7 +131,7 @@ namespace Ryujinx.Core.OsHle.Kernel
Handles[HandlesCount] = WaitEvent; Handles[HandlesCount] = WaitEvent;
Process.Scheduler.Suspend(CurrThread.ProcessorId); Process.Scheduler.Suspend(CurrThread);
int HandleIndex; int HandleIndex;
@ -237,7 +237,7 @@ namespace Ryujinx.Core.OsHle.Kernel
if (Session != null) if (Session != null)
{ {
Process.Scheduler.Suspend(CurrThread.ProcessorId); Process.Scheduler.Suspend(CurrThread);
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr);

View File

@ -75,7 +75,7 @@ namespace Ryujinx.Core.OsHle.Kernel
} }
else else
{ {
Process.Scheduler.Suspend(CurrThread.ProcessorId); Process.Scheduler.Suspend(CurrThread);
Thread.Sleep(NsTimeConverter.GetTimeMs(Ns)); Thread.Sleep(NsTimeConverter.GetTimeMs(Ns));
@ -132,7 +132,7 @@ namespace Ryujinx.Core.OsHle.Kernel
private void SvcGetCurrentProcessorNumber(AThreadState ThreadState) private void SvcGetCurrentProcessorNumber(AThreadState ThreadState)
{ {
ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).ProcessorId; ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).ActualCore;
} }
private void SvcGetThreadId(AThreadState ThreadState) private void SvcGetThreadId(AThreadState ThreadState)

View File

@ -260,17 +260,23 @@ namespace Ryujinx.Core.OsHle.Kernel
WaitThread.MutexAddress = MutexAddress; WaitThread.MutexAddress = MutexAddress;
WaitThread.CondVarAddress = CondVarAddress; WaitThread.CondVarAddress = CondVarAddress;
lock (CondVarLock) lock (Process.ThreadArbiterListLock)
{ {
KThread CurrThread = Process.ThreadArbiterList; KThread CurrThread = Process.ThreadArbiterListHead;
if (CurrThread != null) if (CurrThread == null || CurrThread.ActualPriority > WaitThread.ActualPriority)
{
WaitThread.NextCondVarThread = Process.ThreadArbiterListHead;
Process.ThreadArbiterListHead = WaitThread;
}
else
{ {
bool DoInsert = CurrThread != WaitThread; bool DoInsert = CurrThread != WaitThread;
while (CurrThread.NextCondVarThread != null) while (CurrThread.NextCondVarThread != null)
{ {
if (CurrThread.NextCondVarThread.ActualPriority < WaitThread.ActualPriority) if (CurrThread.NextCondVarThread.ActualPriority > WaitThread.ActualPriority)
{ {
break; break;
} }
@ -293,10 +299,6 @@ namespace Ryujinx.Core.OsHle.Kernel
CurrThread.NextCondVarThread = WaitThread; CurrThread.NextCondVarThread = WaitThread;
} }
} }
else
{
Process.ThreadArbiterList = WaitThread;
}
} }
Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
@ -315,10 +317,10 @@ namespace Ryujinx.Core.OsHle.Kernel
private void CondVarSignal(long CondVarAddress, int Count) private void CondVarSignal(long CondVarAddress, int Count)
{ {
lock (CondVarLock) lock (Process.ThreadArbiterListLock)
{ {
KThread PrevThread = null; KThread PrevThread = null;
KThread CurrThread = Process.ThreadArbiterList; KThread CurrThread = Process.ThreadArbiterListHead;
while (CurrThread != null && (Count == -1 || Count > 0)) while (CurrThread != null && (Count == -1 || Count > 0))
{ {
@ -330,7 +332,7 @@ namespace Ryujinx.Core.OsHle.Kernel
} }
else else
{ {
Process.ThreadArbiterList = CurrThread.NextCondVarThread; Process.ThreadArbiterListHead = CurrThread.NextCondVarThread;
} }
CurrThread.NextCondVarThread = null; CurrThread.NextCondVarThread = null;
@ -401,7 +403,7 @@ namespace Ryujinx.Core.OsHle.Kernel
return; return;
} }
if (CurrThread.NextMutexThread.ActualPriority < WaitThread.ActualPriority) if (CurrThread.NextMutexThread.ActualPriority > WaitThread.ActualPriority)
{ {
break; break;
} }

View File

@ -38,7 +38,9 @@ namespace Ryujinx.Core.OsHle
public KProcessScheduler Scheduler { get; private set; } public KProcessScheduler Scheduler { get; private set; }
public KThread ThreadArbiterList { get; set; } public KThread ThreadArbiterListHead { get; set; }
public object ThreadArbiterListLock { get; private set; }
public KProcessHandleTable HandleTable { get; private set; } public KProcessHandleTable HandleTable { get; private set; }
@ -70,6 +72,8 @@ namespace Ryujinx.Core.OsHle
Memory = new AMemory(); Memory = new AMemory();
ThreadArbiterListLock = new object();
HandleTable = new KProcessHandleTable(); HandleTable = new KProcessHandleTable();
AppletState = new AppletStateMgr(); AppletState = new AppletStateMgr();
@ -196,7 +200,7 @@ namespace Ryujinx.Core.OsHle
AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint); AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint);
KThread Thread = new KThread(CpuThread, ProcessorId, Priority); KThread Thread = new KThread(CpuThread, this, ProcessorId, Priority);
int Handle = HandleTable.OpenHandle(Thread); int Handle = HandleTable.OpenHandle(Thread);