From 3e6afeb5134fc699158e8c4d1646a138a947a93d Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 21 Jun 2018 23:05:42 -0300 Subject: [PATCH] Fix some thread sync issues (#172) * Fix some thread sync issues * Remove some debug stuff * Ensure that writes to the mutex address clears the exclusive monitor --- ChocolArm64/AThread.cs | 2 +- ChocolArm64/Instruction/AInstEmitMemoryEx.cs | 22 +- ChocolArm64/Memory/AMemory.cs | 115 +++++----- .../OsHle/Handles/KProcessScheduler.cs | 42 ++-- Ryujinx.HLE/OsHle/Handles/KThread.cs | 22 +- Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs | 4 +- Ryujinx.HLE/OsHle/Kernel/SvcThread.cs | 25 +-- Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs | 196 +++++++++++------- 8 files changed, 239 insertions(+), 189 deletions(-) diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs index 040f590ea..4fc79d5ef 100644 --- a/ChocolArm64/AThread.cs +++ b/ChocolArm64/AThread.cs @@ -46,7 +46,7 @@ namespace ChocolArm64 { Translator.ExecuteSubroutine(this, EntryPoint); - Memory.RemoveMonitor(ThreadId); + Memory.RemoveMonitor(ThreadState); WorkFinished?.Invoke(this, EventArgs.Empty); }); diff --git a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs index a339bb694..ba8eeceb7 100644 --- a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs +++ b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs @@ -48,6 +48,16 @@ namespace ChocolArm64.Instruction { AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; + if (AccType.HasFlag(AccessType.Ordered)) + { + EmitBarrier(Context); + } + + if (AccType.HasFlag(AccessType.Exclusive)) + { + EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); + } + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdint(Op.Rn); @@ -67,16 +77,6 @@ namespace ChocolArm64.Instruction Context.EmitStintzr(Op.Rt2); } - - if (AccType.HasFlag(AccessType.Exclusive)) - { - EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); - } - - if (AccType.HasFlag(AccessType.Ordered)) - { - EmitBarrier(Context); - } } public static void Pfrm(AILEmitterCtx Context) @@ -150,7 +150,7 @@ namespace ChocolArm64.Instruction Context.EmitLdc_I8(0); Context.EmitStintzr(Op.Rs); - Clrex(Context); + EmitMemoryCall(Context, nameof(AMemory.ClearExclusiveForStore)); } Context.MarkLabel(LblEnd); diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index fe250d4b9..c02bf172f 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; +using System.Threading; namespace ChocolArm64.Memory { @@ -15,32 +16,18 @@ namespace ChocolArm64.Memory public AMemoryMgr Manager { get; private set; } - private struct ExMonitor + private class ArmMonitor { - public long Position { get; private set; } - - private bool ExState; - - public ExMonitor(long Position, bool ExState) - { - this.Position = Position; - this.ExState = ExState; - } + public long Position; + public bool ExState; public bool HasExclusiveAccess(long Position) { return this.Position == Position && ExState; } - - public void Reset() - { - ExState = false; - } } - private Dictionary Monitors; - - private HashSet ExAddrs; + private Dictionary Monitors; public IntPtr Ram { get; private set; } @@ -50,9 +37,7 @@ namespace ChocolArm64.Memory { Manager = new AMemoryMgr(); - Monitors = new Dictionary(); - - ExAddrs = new HashSet(); + Monitors = new Dictionary(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -66,16 +51,13 @@ namespace ChocolArm64.Memory RamPtr = (byte*)Ram; } - public void RemoveMonitor(int ThreadId) + public void RemoveMonitor(AThreadState State) { lock (Monitors) { - if (Monitors.TryGetValue(ThreadId, out ExMonitor Monitor)) - { - ExAddrs.Remove(Monitor.Position); - } + ClearExclusive(State); - Monitors.Remove(ThreadId); + Monitors.Remove(State.ThreadId); } } @@ -85,66 +67,85 @@ namespace ChocolArm64.Memory lock (Monitors) { - if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) + foreach (ArmMonitor Mon in Monitors.Values) { - ExAddrs.Remove(Monitor.Position); + if (Mon.Position == Position && Mon.ExState) + { + Mon.ExState = false; + } } - bool ExState = ExAddrs.Add(Position); - - Monitor = new ExMonitor(Position, ExState); - - if (!Monitors.TryAdd(ThreadState.ThreadId, Monitor)) + if (!Monitors.TryGetValue(ThreadState.ThreadId, out ArmMonitor ThreadMon)) { - Monitors[ThreadState.ThreadId] = Monitor; + ThreadMon = new ArmMonitor(); + + Monitors.Add(ThreadState.ThreadId, ThreadMon); } + + ThreadMon.Position = Position; + ThreadMon.ExState = true; } } public bool TestExclusive(AThreadState ThreadState, long Position) { + //Note: Any call to this method also should be followed by a + //call to ClearExclusiveForStore if this method returns true. Position &= ~ErgMask; - lock (Monitors) - { - if (!Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) - { - return false; - } + Monitor.Enter(Monitors); - return Monitor.HasExclusiveAccess(Position); + if (!Monitors.TryGetValue(ThreadState.ThreadId, out ArmMonitor ThreadMon)) + { + return false; } + + bool ExState = ThreadMon.HasExclusiveAccess(Position); + + if (!ExState) + { + Monitor.Exit(Monitors); + } + + return ExState; + } + + public void ClearExclusiveForStore(AThreadState ThreadState) + { + if (Monitors.TryGetValue(ThreadState.ThreadId, out ArmMonitor ThreadMon)) + { + ThreadMon.ExState = false; + } + + Monitor.Exit(Monitors); } public void ClearExclusive(AThreadState ThreadState) { lock (Monitors) { - if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) + if (Monitors.TryGetValue(ThreadState.ThreadId, out ArmMonitor ThreadMon)) { - Monitor.Reset(); - ExAddrs.Remove(Monitor.Position); + ThreadMon.ExState = false; } } } - public bool AcquireAddress(long Position) + public void WriteInt32ToSharedAddr(long Position, int Value) { - Position &= ~ErgMask; + long MaskedPosition = Position & ~ErgMask; lock (Monitors) { - return ExAddrs.Add(Position); - } - } + foreach (ArmMonitor Mon in Monitors.Values) + { + if (Mon.Position == MaskedPosition && Mon.ExState) + { + Mon.ExState = false; + } + } - public void ReleaseAddress(long Position) - { - Position &= ~ErgMask; - - lock (Monitors) - { - ExAddrs.Remove(Position); + WriteInt32(Position, Value); } } diff --git a/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs index fa414f68d..722460b1e 100644 --- a/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs @@ -127,20 +127,32 @@ namespace Ryujinx.HLE.OsHle.Handles AllThreads[Thread].WaitSync.Set(); } - public void TryToRun(KThread Thread) + public void ChangeCore(KThread Thread, int IdealCore, int CoreMask) { lock (SchedLock) { - if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + if (IdealCore != -3) { + Thread.IdealCore = IdealCore; + } + + Thread.CoreMask = CoreMask; + + if (AllThreads.ContainsKey(Thread)) + { + SetReschedule(Thread.ActualCore); + + SchedulerThread SchedThread = AllThreads[Thread]; + + //Note: Aways if the thread is on the queue first, and try + //adding to a new core later, to ensure that a thread that + //is already running won't be added to another core. if (WaitingToRun.HasThread(SchedThread) && TryAddToCore(Thread)) { + WaitingToRun.Remove(SchedThread); + RunThread(SchedThread); } - else - { - SetReschedule(Thread.ProcessorId); - } } } } @@ -216,18 +228,18 @@ namespace Ryujinx.HLE.OsHle.Handles SchedulerThread NewThread = WaitingToRun.Pop(ActualCore, MinPriority); - if (NewThread == null) + if (NewThread != null) { - PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run."); + NewThread.Thread.ActualCore = ActualCore; - return; + CoreThreads[ActualCore] = NewThread.Thread; + + RunThread(NewThread); + } + else + { + CoreThreads[ActualCore] = null; } - - NewThread.Thread.ActualCore = ActualCore; - - CoreThreads[ActualCore] = NewThread.Thread; - - RunThread(NewThread); } Resume(Thread); diff --git a/Ryujinx.HLE/OsHle/Handles/KThread.cs b/Ryujinx.HLE/OsHle/Handles/KThread.cs index d26a52f04..a135bb282 100644 --- a/Ryujinx.HLE/OsHle/Handles/KThread.cs +++ b/Ryujinx.HLE/OsHle/Handles/KThread.cs @@ -58,25 +58,31 @@ namespace Ryujinx.HLE.OsHle.Handles public void UpdatePriority() { - int OldPriority = ActualPriority; - - int CurrPriority = WantedPriority; + bool PriorityChanged; lock (Process.ThreadSyncLock) { + int OldPriority = ActualPriority; + + int CurrPriority = WantedPriority; + foreach (KThread Thread in MutexWaiters) { - if (CurrPriority > Thread.WantedPriority) + int WantedPriority = Thread.WantedPriority; + + if (CurrPriority > WantedPriority) { - CurrPriority = Thread.WantedPriority; + CurrPriority = WantedPriority; } } + + PriorityChanged = CurrPriority != OldPriority; + + ActualPriority = CurrPriority; } - if (CurrPriority != OldPriority) + if (PriorityChanged) { - ActualPriority = CurrPriority; - Process.Scheduler.Resort(this); MutexOwner?.UpdatePriority(); diff --git a/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs b/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs index 3cb82f0d4..09dcb3d08 100644 --- a/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs +++ b/Ryujinx.HLE/OsHle/Handles/ThreadQueue.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.OsHle.Handles return; } - if (Head == null || Head.Thread.ActualPriority > Wait.Thread.ActualPriority) + if (Head == null || Head.Thread.ActualPriority >= Wait.Thread.ActualPriority) { Wait.Next = Head; @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.OsHle.Handles while (Curr.Next != null) { - if (Curr.Next.Thread.ActualPriority > Wait.Thread.ActualPriority) + if (Curr.Next.Thread.ActualPriority >= Wait.Thread.ActualPriority) { break; } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs index 9dcfaaeed..4501867f4 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs @@ -83,6 +83,8 @@ namespace Ryujinx.HLE.OsHle.Kernel { ulong TimeoutNs = ThreadState.X0; + Ns.Log.PrintDebug(LogClass.KernelSvc, "Timeout = " + TimeoutNs.ToString("x16")); + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); if (TimeoutNs == 0) @@ -123,6 +125,10 @@ namespace Ryujinx.HLE.OsHle.Kernel int Handle = (int)ThreadState.X0; int Priority = (int)ThreadState.X1; + Ns.Log.PrintDebug(LogClass.KernelSvc, + "Handle = " + Handle .ToString("x8") + ", " + + "Priority = " + Priority.ToString("x8")); + KThread Thread = GetThread(ThreadState.Tpidr, Handle); if (Thread != null) @@ -163,13 +169,6 @@ namespace Ryujinx.HLE.OsHle.Kernel private void SvcSetThreadCoreMask(AThreadState ThreadState) { - //FIXME: This is wrong, but the "correct" way to handle - //this svc causes deadlocks when more often. - //There is probably something wrong with it still. - ThreadState.X0 = 0; - - return; - int Handle = (int)ThreadState.X0; int IdealCore = (int)ThreadState.X1; long CoreMask = (long)ThreadState.X2; @@ -188,7 +187,7 @@ namespace Ryujinx.HLE.OsHle.Kernel CoreMask = 1 << IdealCore; } - else if (IdealCore != -3) + else { if ((uint)IdealCore > 3) { @@ -223,11 +222,7 @@ namespace Ryujinx.HLE.OsHle.Kernel //-1 is used as "don't care", so the IdealCore value is ignored. //-2 is used as "use NPDM default core id" (handled above). //-3 is used as "don't update", the old IdealCore value is kept. - if (IdealCore != -3) - { - Thread.IdealCore = IdealCore; - } - else if ((CoreMask & (1 << Thread.IdealCore)) == 0) + if (IdealCore == -3 && (CoreMask & (1 << Thread.IdealCore)) == 0) { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); @@ -236,9 +231,7 @@ namespace Ryujinx.HLE.OsHle.Kernel return; } - Thread.CoreMask = (int)CoreMask; - - Process.Scheduler.TryToRun(Thread); + Process.Scheduler.ChangeCore(Thread, IdealCore, (int)CoreMask); ThreadState.X0 = 0; } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs index 030e6e3d6..ec9c40e08 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs @@ -2,9 +2,6 @@ using ChocolArm64.State; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Handles; using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; using static Ryujinx.HLE.OsHle.ErrorCode; @@ -143,11 +140,9 @@ namespace Ryujinx.HLE.OsHle.Kernel return; } - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + KThread WaitThread = Process.GetThread(ThreadState.Tpidr); - MutexUnlock(CurrThread, MutexAddress); - - if (!CondVarWait(CurrThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout)) + if (!CondVarWait(WaitThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout)) { ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); @@ -168,7 +163,7 @@ namespace Ryujinx.HLE.OsHle.Kernel KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - CondVarSignal(CurrThread, CondVarAddress, Count); + CondVarSignal(ThreadState, CurrThread, CondVarAddress, Count); ThreadState.X0 = 0; } @@ -182,7 +177,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { lock (Process.ThreadSyncLock) { - int MutexValue = Process.Memory.ReadInt32(MutexAddress); + int MutexValue = Memory.ReadInt32(MutexAddress); Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8")); @@ -194,7 +189,7 @@ namespace Ryujinx.HLE.OsHle.Kernel CurrThread.WaitHandle = WaitThreadHandle; CurrThread.MutexAddress = MutexAddress; - InsertWaitingMutexThread(OwnerThreadHandle, WaitThread); + InsertWaitingMutexThreadUnsafe(OwnerThreadHandle, WaitThread); } Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); @@ -208,25 +203,29 @@ namespace Ryujinx.HLE.OsHle.Kernel { //This is the new thread that will now own the mutex. //If no threads are waiting for the lock, then it should be null. - KThread OwnerThread = PopThread(CurrThread.MutexWaiters, x => x.MutexAddress == MutexAddress); + (KThread OwnerThread, int Count) = PopMutexThreadUnsafe(CurrThread, MutexAddress); + + if (OwnerThread == CurrThread) + { + throw new InvalidOperationException(); + } if (OwnerThread != null) { //Remove all waiting mutex from the old owner, //and insert then on the new owner. - UpdateMutexOwner(CurrThread, OwnerThread, MutexAddress); + UpdateMutexOwnerUnsafe(CurrThread, OwnerThread, MutexAddress); CurrThread.UpdatePriority(); - int HasListeners = OwnerThread.MutexWaiters.Count > 0 ? MutexHasListenersMask : 0; + int HasListeners = Count >= 2 ? MutexHasListenersMask : 0; - Process.Memory.WriteInt32(MutexAddress, HasListeners | OwnerThread.WaitHandle); + Memory.WriteInt32ToSharedAddr(MutexAddress, HasListeners | OwnerThread.WaitHandle); OwnerThread.WaitHandle = 0; OwnerThread.MutexAddress = 0; OwnerThread.CondVarAddress = 0; - - OwnerThread.MutexOwner = null; + OwnerThread.MutexOwner = null; OwnerThread.UpdatePriority(); @@ -236,7 +235,7 @@ namespace Ryujinx.HLE.OsHle.Kernel } else { - Process.Memory.WriteInt32(MutexAddress, 0); + Memory.WriteInt32ToSharedAddr(MutexAddress, 0); Ns.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!"); } @@ -256,6 +255,8 @@ namespace Ryujinx.HLE.OsHle.Kernel lock (Process.ThreadSyncLock) { + MutexUnlock(WaitThread, MutexAddress); + WaitThread.CondVarSignaled = false; Process.ThreadArbiterList.Add(WaitThread); @@ -269,11 +270,15 @@ namespace Ryujinx.HLE.OsHle.Kernel lock (Process.ThreadSyncLock) { - WaitThread.MutexOwner?.MutexWaiters.Remove(WaitThread); - if (!WaitThread.CondVarSignaled || WaitThread.MutexOwner != null) { - WaitThread.MutexOwner = null; + if (WaitThread.MutexOwner != null) + { + WaitThread.MutexOwner.MutexWaiters.Remove(WaitThread); + WaitThread.MutexOwner.UpdatePriority(); + + WaitThread.MutexOwner = null; + } Process.ThreadArbiterList.Remove(WaitThread); @@ -291,13 +296,17 @@ namespace Ryujinx.HLE.OsHle.Kernel return true; } - private void CondVarSignal(KThread CurrThread, long CondVarAddress, int Count) + private void CondVarSignal( + AThreadState ThreadState, + KThread CurrThread, + long CondVarAddress, + int Count) { lock (Process.ThreadSyncLock) { while (Count == -1 || Count-- > 0) { - KThread WaitThread = PopThread(Process.ThreadArbiterList, x => x.CondVarAddress == CondVarAddress); + KThread WaitThread = PopCondVarThreadUnsafe(CondVarAddress); if (WaitThread == null) { @@ -308,16 +317,37 @@ namespace Ryujinx.HLE.OsHle.Kernel WaitThread.CondVarSignaled = true; - AcquireMutexValue(WaitThread.MutexAddress); + long MutexAddress = WaitThread.MutexAddress; - int MutexValue = Process.Memory.ReadInt32(WaitThread.MutexAddress); + Memory.SetExclusive(ThreadState, MutexAddress); + + int MutexValue = Memory.ReadInt32(MutexAddress); + + while (MutexValue != 0) + { + if (Memory.TestExclusive(ThreadState, MutexAddress)) + { + //Wait until the lock is released. + InsertWaitingMutexThreadUnsafe(MutexValue & ~MutexHasListenersMask, WaitThread); + + Memory.WriteInt32(MutexAddress, MutexValue | MutexHasListenersMask); + + Memory.ClearExclusiveForStore(ThreadState); + + break; + } + + Memory.SetExclusive(ThreadState, MutexAddress); + + MutexValue = Memory.ReadInt32(MutexAddress); + } Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8")); if (MutexValue == 0) { //Give the lock to this thread. - Process.Memory.WriteInt32(WaitThread.MutexAddress, WaitThread.WaitHandle); + Memory.WriteInt32ToSharedAddr(MutexAddress, WaitThread.WaitHandle); WaitThread.WaitHandle = 0; WaitThread.MutexAddress = 0; @@ -329,46 +359,28 @@ namespace Ryujinx.HLE.OsHle.Kernel Process.Scheduler.WakeUp(WaitThread); } - else - { - //Wait until the lock is released. - MutexValue &= ~MutexHasListenersMask; - - InsertWaitingMutexThread(MutexValue, WaitThread); - - MutexValue |= MutexHasListenersMask; - - Process.Memory.WriteInt32(WaitThread.MutexAddress, MutexValue); - } - - ReleaseMutexValue(WaitThread.MutexAddress); } } } - private void UpdateMutexOwner(KThread CurrThread, KThread NewOwner, long MutexAddress) + private void UpdateMutexOwnerUnsafe(KThread CurrThread, KThread NewOwner, long MutexAddress) { //Go through all threads waiting for the mutex, //and update the MutexOwner field to point to the new owner. - lock (Process.ThreadSyncLock) + for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++) { - for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++) + KThread Thread = CurrThread.MutexWaiters[Index]; + + if (Thread.MutexAddress == MutexAddress) { - KThread Thread = CurrThread.MutexWaiters[Index]; + CurrThread.MutexWaiters.RemoveAt(Index--); - if (Thread.MutexAddress == MutexAddress) - { - CurrThread.MutexWaiters.RemoveAt(Index--); - - Thread.MutexOwner = NewOwner; - - InsertWaitingMutexThread(NewOwner, Thread); - } + InsertWaitingMutexThreadUnsafe(NewOwner, Thread); } } } - private void InsertWaitingMutexThread(int OwnerThreadHandle, KThread WaitThread) + private void InsertWaitingMutexThreadUnsafe(int OwnerThreadHandle, KThread WaitThread) { KThread OwnerThread = Process.HandleTable.GetData(OwnerThreadHandle); @@ -379,47 +391,73 @@ namespace Ryujinx.HLE.OsHle.Kernel return; } - InsertWaitingMutexThread(OwnerThread, WaitThread); + InsertWaitingMutexThreadUnsafe(OwnerThread, WaitThread); } - private void InsertWaitingMutexThread(KThread OwnerThread, KThread WaitThread) + private void InsertWaitingMutexThreadUnsafe(KThread OwnerThread, KThread WaitThread) { - lock (Process.ThreadSyncLock) + WaitThread.MutexOwner = OwnerThread; + + if (!OwnerThread.MutexWaiters.Contains(WaitThread)) { - WaitThread.MutexOwner = OwnerThread; + OwnerThread.MutexWaiters.Add(WaitThread); - if (!OwnerThread.MutexWaiters.Contains(WaitThread)) + OwnerThread.UpdatePriority(); + } + } + + private (KThread, int) PopMutexThreadUnsafe(KThread OwnerThread, long MutexAddress) + { + int Count = 0; + + KThread WakeThread = null; + + foreach (KThread Thread in OwnerThread.MutexWaiters) + { + if (Thread.MutexAddress != MutexAddress) { - OwnerThread.MutexWaiters.Add(WaitThread); + continue; + } - OwnerThread.UpdatePriority(); + if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority) + { + WakeThread = Thread; + } + + Count++; + } + + if (WakeThread != null) + { + OwnerThread.MutexWaiters.Remove(WakeThread); + } + + return (WakeThread, Count); + } + + private KThread PopCondVarThreadUnsafe(long CondVarAddress) + { + KThread WakeThread = null; + + foreach (KThread Thread in Process.ThreadArbiterList) + { + if (Thread.CondVarAddress != CondVarAddress) + { + continue; + } + + if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority) + { + WakeThread = Thread; } } - } - private KThread PopThread(List Threads, Func Predicate) - { - KThread Thread = Threads.OrderBy(x => x.ActualPriority).FirstOrDefault(Predicate); - - if (Thread != null) + if (WakeThread != null) { - Threads.Remove(Thread); + Process.ThreadArbiterList.Remove(WakeThread); } - return Thread; - } - - private void AcquireMutexValue(long MutexAddress) - { - while (!Process.Memory.AcquireAddress(MutexAddress)) - { - Thread.Yield(); - } - } - - private void ReleaseMutexValue(long MutexAddress) - { - Process.Memory.ReleaseAddress(MutexAddress); + return WakeThread; } private bool IsPointingInsideKernel(long Address)