From 531cd678e27a1db7cbb2933e5f22ac5dbcf30908 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 21 Apr 2018 15:56:20 -0300 Subject: [PATCH] Add MutexOwner field to keep track of the thread owning the mutex, update wait list when priority changes, other tweaks --- .../OsHle/Handles/KProcessScheduler.cs | 8 +- Ryujinx.Core/OsHle/Handles/KThread.cs | 84 ++++++++++++++++--- Ryujinx.Core/OsHle/Kernel/SvcThread.cs | 2 +- Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs | 22 +++-- 4 files changed, 92 insertions(+), 24 deletions(-) diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index cd17ca12b0..749dce42c5 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -78,9 +78,9 @@ namespace Ryujinx.Core.OsHle.Handles { SchedThread = Threads[Index]; - if (HighestPriority > SchedThread.Thread.Priority) + if (HighestPriority > SchedThread.Thread.ActualPriority) { - HighestPriority = SchedThread.Thread.Priority; + HighestPriority = SchedThread.Thread.ActualPriority; HighestPrioIndex = Index; } @@ -289,7 +289,7 @@ namespace Ryujinx.Core.OsHle.Handles lock (SchedLock) { - SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority); + SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority); if (IsActive(Thread) && SchedThread == null) { @@ -388,7 +388,7 @@ namespace Ryujinx.Core.OsHle.Handles Logging.Debug(LogClass.KernelScheduler, "(" + "ThreadId: " + Thread.ThreadId + ", " + "ProcessorId: " + Thread.ProcessorId + ", " + - "Priority: " + Thread.Priority + ") " + Message); + "Priority: " + Thread.ActualPriority + ") " + Message); } public void Dispose() diff --git a/Ryujinx.Core/OsHle/Handles/KThread.cs b/Ryujinx.Core/OsHle/Handles/KThread.cs index d593b0a965..43ee3f7d28 100644 --- a/Ryujinx.Core/OsHle/Handles/KThread.cs +++ b/Ryujinx.Core/OsHle/Handles/KThread.cs @@ -1,4 +1,5 @@ using ChocolArm64; +using System; namespace Ryujinx.Core.OsHle.Handles { @@ -6,18 +7,19 @@ namespace Ryujinx.Core.OsHle.Handles { public AThread Thread { get; private set; } + public KThread MutexOwner { get; set; } + public KThread NextMutexThread { get; set; } public KThread NextCondVarThread { get; set; } public long MutexAddress { get; set; } public long CondVarAddress { get; set; } + public int ActualPriority { get; private set; } + public int WantedPriority { get; private set; } + public int ProcessorId { get; private set; } - public int Priority { get; private set; } - - private int DesiredPriority; - public int WaitHandle { get; set; } public int ThreadId => Thread.ThreadId; @@ -32,21 +34,79 @@ namespace Ryujinx.Core.OsHle.Handles public void SetPriority(int Priority) { - this.Priority = DesiredPriority = Priority; + ActualPriority = WantedPriority = Priority; UpdatePriority(); } - public void ResetPriority() - { - Priority = DesiredPriority; - } - public void UpdatePriority() { - if (Priority > (NextMutexThread?.Priority ?? Priority)) + int OldPriority = ActualPriority; + + int CurrPriority = WantedPriority; + + if (NextMutexThread != null && CurrPriority > NextMutexThread.WantedPriority) { - Priority = NextMutexThread.Priority; + CurrPriority = NextMutexThread.WantedPriority; + } + + if (CurrPriority != OldPriority) + { + ActualPriority = CurrPriority; + + UpdateWaitList(); + + MutexOwner?.UpdatePriority(); + } + } + + private void UpdateWaitList() + { + KThread OwnerThread = MutexOwner; + + if (OwnerThread != null) + { + //The MutexOwner field should only be non null when the thread is + //waiting for the lock, and the lock belongs to another thread. + if (OwnerThread == this) + { + throw new InvalidOperationException(); + } + + lock (OwnerThread) + { + //Remove itself from the list. + KThread CurrThread = OwnerThread; + + while (CurrThread.NextMutexThread != null) + { + if (CurrThread.NextMutexThread == this) + { + CurrThread.NextMutexThread = NextMutexThread; + + break; + } + + CurrThread = CurrThread.NextMutexThread; + } + + //Re-add taking new priority into account. + CurrThread = OwnerThread; + + while (CurrThread.NextMutexThread != null) + { + if (CurrThread.NextMutexThread.ActualPriority < ActualPriority) + { + break; + } + + CurrThread = CurrThread.NextMutexThread; + } + + NextMutexThread = CurrThread.NextMutexThread; + + CurrThread.NextMutexThread = this; + } } } } diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs index 33092f259d..1e4d61b4fc 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs @@ -91,7 +91,7 @@ namespace Ryujinx.Core.OsHle.Kernel if (CurrThread != null) { ThreadState.X0 = 0; - ThreadState.X1 = (ulong)CurrThread.Priority; + ThreadState.X1 = (ulong)CurrThread.ActualPriority; } else { diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs index 3482ebe0ff..6502e4c989 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Core.OsHle.Kernel //If no threads are waiting for the lock, then it should be null. KThread OwnerThread = CurrThread.NextMutexThread; - while (OwnerThread?.NextMutexThread != null && OwnerThread.MutexAddress != MutexAddress) + while (OwnerThread != null && OwnerThread.MutexAddress != MutexAddress) { OwnerThread = OwnerThread.NextMutexThread; } @@ -210,7 +210,9 @@ namespace Ryujinx.Core.OsHle.Kernel OwnerThread.MutexAddress = 0; OwnerThread.CondVarAddress = 0; - OwnerThread.ResetPriority(); + OwnerThread.MutexOwner = null; + + OwnerThread.UpdatePriority(); Process.Scheduler.WakeUp(OwnerThread); @@ -246,7 +248,7 @@ namespace Ryujinx.Core.OsHle.Kernel while (CurrThread.NextCondVarThread != null) { - if (CurrThread.NextCondVarThread.Priority < WaitThread.Priority) + if (CurrThread.NextCondVarThread.ActualPriority < WaitThread.ActualPriority) { break; } @@ -315,18 +317,22 @@ namespace Ryujinx.Core.OsHle.Kernel if (MutexValue == 0) { + //Give the lock to this thread. Process.Memory.WriteInt32(CurrThread.MutexAddress, CurrThread.WaitHandle); CurrThread.WaitHandle = 0; CurrThread.MutexAddress = 0; CurrThread.CondVarAddress = 0; - CurrThread.ResetPriority(); + CurrThread.MutexOwner = null; + + CurrThread.UpdatePriority(); Process.Scheduler.WakeUp(CurrThread); } else { + //Wait until the lock is released. InsertWaitingMutexThread(MutexValue, CurrThread); MutexValue |= MutexHasListenersMask; @@ -356,6 +362,8 @@ namespace Ryujinx.Core.OsHle.Kernel return; } + WaitThread.MutexOwner = OwnerThread; + lock (OwnerThread) { KThread CurrThread = OwnerThread; @@ -367,7 +375,7 @@ namespace Ryujinx.Core.OsHle.Kernel return; } - if (CurrThread.NextMutexThread.Priority < WaitThread.Priority) + if (CurrThread.NextMutexThread.ActualPriority < WaitThread.ActualPriority) { break; } @@ -384,10 +392,10 @@ namespace Ryujinx.Core.OsHle.Kernel WaitThread.NextMutexThread = CurrThread.NextMutexThread; CurrThread.NextMutexThread = WaitThread; - - CurrThread.UpdatePriority(); } } + + OwnerThread.UpdatePriority(); } private void AcquireMutexValue(long MutexAddress)