forked from Mirror/Ryujinx
Fix some races in SvcThreadSync and change the way how yield works
This commit is contained in:
parent
7a8fbcf89f
commit
73a3233d57
7 changed files with 173 additions and 219 deletions
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Core.Logging;
|
using Ryujinx.Core.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Handles
|
namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
|
@ -10,7 +11,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
|
|
||||||
private ThreadQueue WaitingToRun;
|
private ThreadQueue WaitingToRun;
|
||||||
|
|
||||||
private int ActiveCores;
|
private KThread[] CoreThreads;
|
||||||
|
|
||||||
private object SchedLock;
|
private object SchedLock;
|
||||||
|
|
||||||
|
@ -24,6 +25,8 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
|
|
||||||
WaitingToRun = new ThreadQueue();
|
WaitingToRun = new ThreadQueue();
|
||||||
|
|
||||||
|
CoreThreads = new KThread[4];
|
||||||
|
|
||||||
SchedLock = new object();
|
SchedLock = new object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +41,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AddActiveCore(Thread))
|
if (TryAddToCore(Thread))
|
||||||
{
|
{
|
||||||
Thread.Thread.Execute();
|
Thread.Thread.Execute();
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ActualCore}!");
|
Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ActualCore}!");
|
||||||
|
|
||||||
RemoveActiveCore(ActualCore);
|
CoreThreads[ActualCore] = null;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -104,44 +107,38 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnterWait(KThread Thread)
|
public void EnterWait(KThread Thread, int TimeoutMs = Timeout.Infinite)
|
||||||
{
|
{
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
SchedulerThread SchedThread = AllThreads[Thread];
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Suspend(Thread);
|
Suspend(Thread);
|
||||||
|
|
||||||
SchedThread.WaitSync.WaitOne();
|
SchedThread.WaitSync.WaitOne(TimeoutMs);
|
||||||
|
|
||||||
TryResumingExecution(SchedThread);
|
TryResumingExecution(SchedThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnterWait(KThread Thread, int Timeout)
|
|
||||||
{
|
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Suspend(Thread);
|
|
||||||
|
|
||||||
bool Result = SchedThread.WaitSync.WaitOne(Timeout);
|
|
||||||
|
|
||||||
TryResumingExecution(SchedThread);
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WakeUp(KThread Thread)
|
public void WakeUp(KThread Thread)
|
||||||
{
|
{
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
AllThreads[Thread].WaitSync.Set();
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedThread.WaitSync.Set();
|
public void TryToRun(KThread Thread)
|
||||||
|
{
|
||||||
|
lock (SchedLock)
|
||||||
|
{
|
||||||
|
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||||
|
{
|
||||||
|
if (WaitingToRun.HasThread(SchedThread) && TryAddToCore(Thread))
|
||||||
|
{
|
||||||
|
RunThread(SchedThread);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetReschedule(Thread.ProcessorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Suspend(KThread Thread)
|
public void Suspend(KThread Thread)
|
||||||
|
@ -150,6 +147,8 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "suspended.");
|
PrintDbgThreadInfo(Thread, "suspended.");
|
||||||
|
|
||||||
|
AllThreads[Thread].NeedsReschedule = false;
|
||||||
|
|
||||||
int ActualCore = Thread.ActualCore;
|
int ActualCore = Thread.ActualCore;
|
||||||
|
|
||||||
SchedulerThread SchedThread = WaitingToRun.Pop(ActualCore);
|
SchedulerThread SchedThread = WaitingToRun.Pop(ActualCore);
|
||||||
|
@ -158,84 +157,76 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
SchedThread.Thread.ActualCore = ActualCore;
|
SchedThread.Thread.ActualCore = ActualCore;
|
||||||
|
|
||||||
|
CoreThreads[ActualCore] = SchedThread.Thread;
|
||||||
|
|
||||||
RunThread(SchedThread);
|
RunThread(SchedThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!");
|
Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!");
|
||||||
|
|
||||||
RemoveActiveCore(ActualCore);
|
CoreThreads[ActualCore] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Yield(KThread Thread)
|
public void SetReschedule(int Core)
|
||||||
|
{
|
||||||
|
lock (SchedLock)
|
||||||
|
{
|
||||||
|
KThread Thread = CoreThreads[Core];
|
||||||
|
|
||||||
|
if (Thread != null && AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||||
|
{
|
||||||
|
SchedThread.NeedsReschedule = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reschedule(KThread Thread)
|
||||||
|
{
|
||||||
|
SchedulerThread SchedThread = AllThreads[Thread];
|
||||||
|
|
||||||
|
bool NeedsReschedule;
|
||||||
|
|
||||||
|
lock (SchedLock)
|
||||||
|
{
|
||||||
|
NeedsReschedule = SchedThread.NeedsReschedule;
|
||||||
|
|
||||||
|
SchedThread.NeedsReschedule = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NeedsReschedule)
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "yielded execution.");
|
PrintDbgThreadInfo(Thread, "yielded execution.");
|
||||||
|
|
||||||
if (IsActive(Thread))
|
|
||||||
{
|
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
int ActualCore = Thread.ActualCore;
|
int ActualCore = Thread.ActualCore;
|
||||||
|
|
||||||
SchedulerThread SchedThread = WaitingToRun.Pop(ActualCore, Thread.ActualPriority);
|
SchedulerThread NewThread = WaitingToRun.Pop(ActualCore, Thread.ActualPriority);
|
||||||
|
|
||||||
if (SchedThread == null)
|
if (NewThread == null)
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
|
PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SchedThread != null)
|
NewThread.Thread.ActualCore = ActualCore;
|
||||||
{
|
|
||||||
SchedThread.Thread.ActualCore = ActualCore;
|
|
||||||
|
|
||||||
RunThread(SchedThread);
|
CoreThreads[ActualCore] = NewThread.Thread;
|
||||||
}
|
|
||||||
}
|
RunThread(NewThread);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Just stop running the thread if it's not active,
|
|
||||||
//and run whatever is waiting to run with the higuest priority.
|
|
||||||
Suspend(Thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(Thread);
|
TryResumingExecution(SchedThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRunning(KThread Thread)
|
|
||||||
{
|
|
||||||
//Failing to get the thread here is fine,
|
|
||||||
//the thread may not have been started yet.
|
|
||||||
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
|
||||||
{
|
|
||||||
lock (SchedLock)
|
|
||||||
{
|
|
||||||
if (WaitingToRun.HasThread(SchedThread) && AddActiveCore(Thread))
|
|
||||||
{
|
|
||||||
WaitingToRun.Remove(SchedThread);
|
|
||||||
|
|
||||||
RunThread(SchedThread);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume(KThread Thread)
|
public void Resume(KThread Thread)
|
||||||
{
|
{
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
TryResumingExecution(AllThreads[Thread]);
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
TryResumingExecution(SchedThread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryResumingExecution(SchedulerThread SchedThread)
|
private void TryResumingExecution(SchedulerThread SchedThread)
|
||||||
|
@ -248,7 +239,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
|
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
if (AddActiveCore(Thread))
|
if (TryAddToCore(Thread))
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "resuming execution...");
|
PrintDbgThreadInfo(Thread, "resuming execution...");
|
||||||
|
|
||||||
|
@ -257,6 +248,8 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
|
|
||||||
WaitingToRun.Push(SchedThread);
|
WaitingToRun.Push(SchedThread);
|
||||||
|
|
||||||
|
SetReschedule(Thread.ProcessorId);
|
||||||
|
|
||||||
PrintDbgThreadInfo(Thread, "entering wait state...");
|
PrintDbgThreadInfo(Thread, "entering wait state...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,72 +280,37 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsActive(KThread Thread)
|
private bool TryAddToCore(KThread Thread)
|
||||||
{
|
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return SchedThread.IsActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool AddActiveCore(KThread Thread)
|
|
||||||
{
|
|
||||||
int CoreMask;
|
|
||||||
|
|
||||||
lock (SchedLock)
|
|
||||||
{
|
{
|
||||||
//First, try running it on Ideal Core.
|
//First, try running it on Ideal Core.
|
||||||
int IdealCore = Thread.IdealCore;
|
int IdealCore = Thread.IdealCore;
|
||||||
|
|
||||||
if (IdealCore != -1)
|
if (IdealCore != -1 && CoreThreads[IdealCore] == null)
|
||||||
{
|
{
|
||||||
CoreMask = 1 << IdealCore;
|
|
||||||
|
|
||||||
if ((ActiveCores & CoreMask) == 0)
|
|
||||||
{
|
|
||||||
ActiveCores |= CoreMask;
|
|
||||||
|
|
||||||
Thread.ActualCore = IdealCore;
|
Thread.ActualCore = IdealCore;
|
||||||
|
|
||||||
|
CoreThreads[IdealCore] = Thread;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//If that fails, then try running on any core allowed by Core Mask.
|
//If that fails, then try running on any core allowed by Core Mask.
|
||||||
CoreMask = Thread.CoreMask & ~ActiveCores;
|
int CoreMask = Thread.CoreMask;
|
||||||
|
|
||||||
if (CoreMask != 0)
|
for (int Core = 0; Core < CoreThreads.Length; Core++, CoreMask >>= 1)
|
||||||
{
|
{
|
||||||
CoreMask &= -CoreMask;
|
if ((CoreMask & 1) != 0 && CoreThreads[Core] == null)
|
||||||
|
{
|
||||||
|
Thread.ActualCore = Core;
|
||||||
|
|
||||||
ActiveCores |= CoreMask;
|
CoreThreads[Core] = Thread;
|
||||||
|
|
||||||
for (int Bit = 0; Bit < 32; Bit++)
|
|
||||||
{
|
|
||||||
if (((CoreMask >> Bit) & 1) != 0)
|
|
||||||
{
|
|
||||||
Thread.ActualCore = Bit;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
public long MutexAddress { get; set; }
|
public long MutexAddress { get; set; }
|
||||||
public long CondVarAddress { get; set; }
|
public long CondVarAddress { get; set; }
|
||||||
|
|
||||||
|
public bool CondVarSignaled { get; set; }
|
||||||
|
|
||||||
private Process Process;
|
private Process Process;
|
||||||
|
|
||||||
public List<KThread> MutexWaiters { get; private set; }
|
public List<KThread> MutexWaiters { get; private set; }
|
||||||
|
@ -22,6 +24,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
public int WantedPriority { get; private set; }
|
public int WantedPriority { get; private set; }
|
||||||
|
|
||||||
public int ActualCore { get; set; }
|
public int ActualCore { get; set; }
|
||||||
|
public int ProcessorId { get; set; }
|
||||||
public int IdealCore { get; set; }
|
public int IdealCore { get; set; }
|
||||||
|
|
||||||
public int WaitHandle { get; set; }
|
public int WaitHandle { get; set; }
|
||||||
|
@ -31,16 +34,17 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
public KThread(
|
public KThread(
|
||||||
AThread Thread,
|
AThread Thread,
|
||||||
Process Process,
|
Process Process,
|
||||||
int IdealCore,
|
int ProcessorId,
|
||||||
int Priority)
|
int Priority)
|
||||||
{
|
{
|
||||||
this.Thread = Thread;
|
this.Thread = Thread;
|
||||||
this.Process = Process;
|
this.Process = Process;
|
||||||
this.IdealCore = IdealCore;
|
this.ProcessorId = ProcessorId;
|
||||||
|
this.IdealCore = ProcessorId;
|
||||||
|
|
||||||
MutexWaiters = new List<KThread>();
|
MutexWaiters = new List<KThread>();
|
||||||
|
|
||||||
CoreMask = 1 << IdealCore;
|
CoreMask = 1 << ProcessorId;
|
||||||
|
|
||||||
ActualPriority = WantedPriority = Priority;
|
ActualPriority = WantedPriority = Priority;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
|
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
public bool NeedsReschedule { get; set; }
|
||||||
|
|
||||||
public AutoResetEvent WaitSync { get; private set; }
|
public AutoResetEvent WaitSync { get; private set; }
|
||||||
public ManualResetEvent WaitActivity { get; private set; }
|
public ManualResetEvent WaitActivity { get; private set; }
|
||||||
public AutoResetEvent WaitSched { get; private set; }
|
public AutoResetEvent WaitSched { get; private set; }
|
||||||
|
|
|
@ -96,6 +96,8 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
Func(ThreadState);
|
Func(ThreadState);
|
||||||
|
|
||||||
|
Process.Scheduler.Reschedule(Process.GetThread(ThreadState.Tpidr));
|
||||||
|
|
||||||
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
|
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -55,13 +55,12 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
|
|
||||||
KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
|
KThread NewThread = Process.HandleTable.GetData<KThread>(Handle);
|
||||||
|
|
||||||
if (CurrThread != null)
|
if (NewThread != null)
|
||||||
{
|
{
|
||||||
Process.Scheduler.StartThread(CurrThread);
|
Process.Scheduler.StartThread(NewThread);
|
||||||
|
Process.Scheduler.SetReschedule(NewThread.ProcessorId);
|
||||||
Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr));
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
@ -82,19 +81,19 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
private void SvcSleepThread(AThreadState ThreadState)
|
private void SvcSleepThread(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
ulong Ns = ThreadState.X0;
|
ulong TimeoutNs = ThreadState.X0;
|
||||||
|
|
||||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||||
|
|
||||||
if (Ns == 0)
|
if (TimeoutNs == 0)
|
||||||
{
|
{
|
||||||
Process.Scheduler.Yield(CurrThread);
|
Process.Scheduler.SetReschedule(CurrThread.ActualCore);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Process.Scheduler.Suspend(CurrThread);
|
Process.Scheduler.Suspend(CurrThread);
|
||||||
|
|
||||||
Thread.Sleep(NsTimeConverter.GetTimeMs(Ns));
|
Thread.Sleep(NsTimeConverter.GetTimeMs(TimeoutNs));
|
||||||
|
|
||||||
Process.Scheduler.Resume(CurrThread);
|
Process.Scheduler.Resume(CurrThread);
|
||||||
}
|
}
|
||||||
|
@ -210,15 +209,7 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
Thread.CoreMask = (int)CoreMask;
|
Thread.CoreMask = (int)CoreMask;
|
||||||
|
|
||||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
Process.Scheduler.TryToRun(Thread);
|
||||||
|
|
||||||
//Try yielding execution, for the case where the new
|
|
||||||
//core mask allows the thread to run on the current core.
|
|
||||||
Process.Scheduler.Yield(CurrThread);
|
|
||||||
|
|
||||||
//Try running the modified thread, for the case where one
|
|
||||||
//of the cores specified on the core mask is free.
|
|
||||||
Process.Scheduler.TryRunning(Thread);
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,7 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress))
|
MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress);
|
||||||
{
|
|
||||||
Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
@ -157,8 +154,6 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr));
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +162,13 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
long CondVarAddress = (long)ThreadState.X0;
|
long CondVarAddress = (long)ThreadState.X0;
|
||||||
int Count = (int)ThreadState.X1;
|
int Count = (int)ThreadState.X1;
|
||||||
|
|
||||||
CondVarSignal(CondVarAddress, Count);
|
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||||
|
"CondVarAddress = " + CondVarAddress.ToString("x16") + ", " +
|
||||||
|
"Count = " + Count .ToString("x8"));
|
||||||
|
|
||||||
|
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||||
|
|
||||||
|
CondVarSignal(CurrThread, CondVarAddress, Count);
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
@ -178,13 +179,13 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
int OwnerThreadHandle,
|
int OwnerThreadHandle,
|
||||||
int WaitThreadHandle,
|
int WaitThreadHandle,
|
||||||
long MutexAddress)
|
long MutexAddress)
|
||||||
|
{
|
||||||
|
lock (Process.ThreadSyncLock)
|
||||||
{
|
{
|
||||||
int MutexValue = Process.Memory.ReadInt32(MutexAddress);
|
int MutexValue = Process.Memory.ReadInt32(MutexAddress);
|
||||||
|
|
||||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
|
||||||
|
|
||||||
lock (Process.ThreadSyncLock)
|
|
||||||
{
|
|
||||||
if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
|
if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -201,20 +202,13 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
Process.Scheduler.EnterWait(CurrThread);
|
Process.Scheduler.EnterWait(CurrThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MutexUnlock(KThread CurrThread, long MutexAddress)
|
private void MutexUnlock(KThread CurrThread, long MutexAddress)
|
||||||
{
|
{
|
||||||
if (CurrThread == null)
|
|
||||||
{
|
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex 0x{MutexAddress:x16}!");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (Process.ThreadSyncLock)
|
lock (Process.ThreadSyncLock)
|
||||||
{
|
{
|
||||||
//This is the new thread that will not own the mutex.
|
//This is the new thread that will not own the mutex.
|
||||||
//If no threads are waiting for the lock, then it should be null.
|
//If no threads are waiting for the lock, then it should be null.
|
||||||
KThread OwnerThread = GetHighestPriority(CurrThread.MutexWaiters, MutexAddress);
|
KThread OwnerThread = PopThread(CurrThread.MutexWaiters, x => x.MutexAddress == MutexAddress);
|
||||||
|
|
||||||
if (OwnerThread != null)
|
if (OwnerThread != null)
|
||||||
{
|
{
|
||||||
|
@ -240,16 +234,12 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
Process.Scheduler.WakeUp(OwnerThread);
|
Process.Scheduler.WakeUp(OwnerThread);
|
||||||
|
|
||||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!");
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!");
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Process.Memory.WriteInt32(MutexAddress, 0);
|
Process.Memory.WriteInt32(MutexAddress, 0);
|
||||||
|
|
||||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!");
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!");
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,8 +255,10 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
WaitThread.MutexAddress = MutexAddress;
|
WaitThread.MutexAddress = MutexAddress;
|
||||||
WaitThread.CondVarAddress = CondVarAddress;
|
WaitThread.CondVarAddress = CondVarAddress;
|
||||||
|
|
||||||
lock (Process.ThreadArbiterListLock)
|
lock (Process.ThreadArbiterList)
|
||||||
{
|
{
|
||||||
|
WaitThread.CondVarSignaled = false;
|
||||||
|
|
||||||
Process.ThreadArbiterList.Add(WaitThread);
|
Process.ThreadArbiterList.Add(WaitThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,58 +266,77 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
if (Timeout != ulong.MaxValue)
|
if (Timeout != ulong.MaxValue)
|
||||||
{
|
{
|
||||||
return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
|
Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
|
||||||
|
|
||||||
|
lock (Process.ThreadArbiterList)
|
||||||
|
{
|
||||||
|
if (!WaitThread.CondVarSignaled)
|
||||||
|
{
|
||||||
|
Process.ThreadArbiterList.Remove(WaitThread);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Process.Scheduler.EnterWait(WaitThread);
|
Process.Scheduler.EnterWait(WaitThread);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CondVarSignal(KThread CurrThread, long CondVarAddress, int Count)
|
||||||
|
{
|
||||||
|
lock (Process.ThreadArbiterList)
|
||||||
|
{
|
||||||
|
while (Count == -1 || Count-- > 0)
|
||||||
|
{
|
||||||
|
KThread WaitThread = PopThread(Process.ThreadArbiterList, x => x.CondVarAddress == CondVarAddress);
|
||||||
|
|
||||||
|
if (WaitThread == null)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "No more threads to wake up!");
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CondVarSignal(long CondVarAddress, int Count)
|
WaitThread.CondVarSignaled = true;
|
||||||
{
|
|
||||||
lock (Process.ThreadArbiterListLock)
|
|
||||||
{
|
|
||||||
KThread CurrThread = GetHighestPriority(CondVarAddress);
|
|
||||||
|
|
||||||
while (CurrThread != null && (Count == -1 || Count-- > 0))
|
AcquireMutexValue(WaitThread.MutexAddress);
|
||||||
{
|
|
||||||
AcquireMutexValue(CurrThread.MutexAddress);
|
|
||||||
|
|
||||||
int MutexValue = Process.Memory.ReadInt32(CurrThread.MutexAddress);
|
int MutexValue = Process.Memory.ReadInt32(WaitThread.MutexAddress);
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
|
||||||
|
|
||||||
if (MutexValue == 0)
|
if (MutexValue == 0)
|
||||||
{
|
{
|
||||||
//Give the lock to this thread.
|
//Give the lock to this thread.
|
||||||
Process.Memory.WriteInt32(CurrThread.MutexAddress, CurrThread.WaitHandle);
|
Process.Memory.WriteInt32(WaitThread.MutexAddress, WaitThread.WaitHandle);
|
||||||
|
|
||||||
CurrThread.WaitHandle = 0;
|
WaitThread.WaitHandle = 0;
|
||||||
CurrThread.MutexAddress = 0;
|
WaitThread.MutexAddress = 0;
|
||||||
CurrThread.CondVarAddress = 0;
|
WaitThread.CondVarAddress = 0;
|
||||||
|
|
||||||
CurrThread.MutexOwner?.UpdatePriority();
|
WaitThread.MutexOwner?.UpdatePriority();
|
||||||
|
|
||||||
CurrThread.MutexOwner = null;
|
WaitThread.MutexOwner = null;
|
||||||
|
|
||||||
Process.Scheduler.WakeUp(CurrThread);
|
Process.Scheduler.WakeUp(WaitThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Wait until the lock is released.
|
//Wait until the lock is released.
|
||||||
MutexValue &= ~MutexHasListenersMask;
|
MutexValue &= ~MutexHasListenersMask;
|
||||||
|
|
||||||
InsertWaitingMutexThread(MutexValue, CurrThread);
|
InsertWaitingMutexThread(MutexValue, WaitThread);
|
||||||
|
|
||||||
MutexValue |= MutexHasListenersMask;
|
MutexValue |= MutexHasListenersMask;
|
||||||
|
|
||||||
Process.Memory.WriteInt32(CurrThread.MutexAddress, MutexValue);
|
Process.Memory.WriteInt32(WaitThread.MutexAddress, MutexValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseMutexValue(CurrThread.MutexAddress);
|
ReleaseMutexValue(WaitThread.MutexAddress);
|
||||||
|
|
||||||
CurrThread = GetHighestPriority(CondVarAddress);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,17 +392,7 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private KThread GetHighestPriority(List<KThread> Threads, long MutexAddress)
|
private KThread PopThread(List<KThread> Threads, Func<KThread, bool> Predicate)
|
||||||
{
|
|
||||||
return GetHighestPriority(Threads, x => x.MutexAddress == MutexAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
private KThread GetHighestPriority(long CondVarAddress)
|
|
||||||
{
|
|
||||||
return GetHighestPriority(Process.ThreadArbiterList, x => x.CondVarAddress == CondVarAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
private KThread GetHighestPriority(List<KThread> Threads, Func<KThread, bool> Predicate)
|
|
||||||
{
|
{
|
||||||
KThread Thread = Threads.OrderBy(x => x.ActualPriority).FirstOrDefault(Predicate);
|
KThread Thread = Threads.OrderBy(x => x.ActualPriority).FirstOrDefault(Predicate);
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,6 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
public List<KThread> ThreadArbiterList { get; private set; }
|
public List<KThread> ThreadArbiterList { get; private set; }
|
||||||
|
|
||||||
public object ThreadArbiterListLock { get; private set; }
|
|
||||||
|
|
||||||
public object ThreadSyncLock { get; private set; }
|
public object ThreadSyncLock { get; private set; }
|
||||||
|
|
||||||
public KProcessHandleTable HandleTable { get; private set; }
|
public KProcessHandleTable HandleTable { get; private set; }
|
||||||
|
@ -76,8 +74,6 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
ThreadArbiterList = new List<KThread>();
|
ThreadArbiterList = new List<KThread>();
|
||||||
|
|
||||||
ThreadArbiterListLock = new object();
|
|
||||||
|
|
||||||
ThreadSyncLock = new object();
|
ThreadSyncLock = new object();
|
||||||
|
|
||||||
HandleTable = new KProcessHandleTable();
|
HandleTable = new KProcessHandleTable();
|
||||||
|
|
Loading…
Reference in a new issue