diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index cd17ca12b0..2f69460018 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -13,23 +13,17 @@ namespace Ryujinx.Core.OsHle.Handles { public KThread Thread { get; private set; } - public ManualResetEvent SyncWaitEvent { get; private set; } - public AutoResetEvent SchedWaitEvent { get; private set; } + public AutoResetEvent WaitEvent { get; private set; } public bool Active { get; set; } - public int SyncTimeout { get; set; } - public SchedulerThread(KThread Thread) { this.Thread = Thread; - SyncWaitEvent = new ManualResetEvent(true); - SchedWaitEvent = new AutoResetEvent(false); + WaitEvent = new AutoResetEvent(false); Active = true; - - SyncTimeout = 0; } public void Dispose() @@ -41,8 +35,7 @@ namespace Ryujinx.Core.OsHle.Handles { if (Disposing) { - SyncWaitEvent.Dispose(); - SchedWaitEvent.Dispose(); + WaitEvent.Dispose(); } } } @@ -201,66 +194,45 @@ namespace Ryujinx.Core.OsHle.Handles throw new InvalidOperationException(); } - SchedThread.Active = Active; - - UpdateSyncWaitEvent(SchedThread); - - WaitIfNeeded(SchedThread); - } - - public bool EnterWait(KThread Thread, int Timeout = -1) - { - if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + lock (SchedLock) { - throw new InvalidOperationException(); + bool OldState = SchedThread.Active; + + SchedThread.Active = Active; + + if (!OldState && Active) + { + if (ActiveProcessors.Add(Thread.ProcessorId)) + { + RunThread(SchedThread); + } + else + { + WaitingToRun[Thread.ProcessorId].Push(SchedThread); + + PrintDbgThreadInfo(Thread, "entering wait state..."); + } + } + else if (OldState && !Active) + { + if (Thread.Thread.IsCurrentThread()) + { + Suspend(Thread.ProcessorId); + + PrintDbgThreadInfo(Thread, "entering inactive wait state..."); + } + else + { + WaitingToRun[Thread.ProcessorId].Remove(SchedThread); + } + } } - SchedThread.SyncTimeout = Timeout; - - UpdateSyncWaitEvent(SchedThread); - - return WaitIfNeeded(SchedThread); - } - - public void WakeUp(KThread Thread) - { - if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + if (!Active && Thread.Thread.IsCurrentThread()) { - throw new InvalidOperationException(); - } + SchedThread.WaitEvent.WaitOne(); - SchedThread.SyncTimeout = 0; - - UpdateSyncWaitEvent(SchedThread); - - WaitIfNeeded(SchedThread); - } - - private void UpdateSyncWaitEvent(SchedulerThread SchedThread) - { - if (SchedThread.Active && SchedThread.SyncTimeout == 0) - { - SchedThread.SyncWaitEvent.Set(); - } - else - { - SchedThread.SyncWaitEvent.Reset(); - } - } - - private bool WaitIfNeeded(SchedulerThread SchedThread) - { - KThread Thread = SchedThread.Thread; - - if (!IsActive(SchedThread) && Thread.Thread.IsCurrentThread()) - { - Suspend(Thread.ProcessorId); - - return Resume(Thread); - } - else - { - return false; + PrintDbgThreadInfo(Thread, "resuming execution..."); } } @@ -291,76 +263,64 @@ namespace Ryujinx.Core.OsHle.Handles { SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority); - if (IsActive(Thread) && SchedThread == null) + if (SchedThread == null) { PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run."); return; } - if (SchedThread != null) - { - RunThread(SchedThread); - } + RunThread(SchedThread); } Resume(Thread); } - public bool Resume(KThread Thread) + public void Resume(KThread Thread) { if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) { throw new InvalidOperationException(); } - return TryResumingExecution(SchedThread); + TryResumingExecution(SchedThread); } - private bool TryResumingExecution(SchedulerThread SchedThread) + private void TryResumingExecution(SchedulerThread SchedThread) { KThread Thread = SchedThread.Thread; - if (!SchedThread.Active || SchedThread.SyncTimeout != 0) + if (SchedThread.Active) + { + lock (SchedLock) + { + if (ActiveProcessors.Add(Thread.ProcessorId)) + { + PrintDbgThreadInfo(Thread, "resuming execution..."); + + return; + } + + WaitingToRun[Thread.ProcessorId].Push(SchedThread); + + PrintDbgThreadInfo(Thread, "entering wait state..."); + } + } + else { PrintDbgThreadInfo(Thread, "entering inactive wait state..."); } - bool Result = false; - - if (SchedThread.SyncTimeout != 0) - { - Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout); - - SchedThread.SyncTimeout = 0; - } - - lock (SchedLock) - { - if (ActiveProcessors.Add(Thread.ProcessorId)) - { - PrintDbgThreadInfo(Thread, "resuming execution..."); - - return Result; - } - - WaitingToRun[Thread.ProcessorId].Push(SchedThread); - - PrintDbgThreadInfo(Thread, "entering wait state..."); - } - - SchedThread.SchedWaitEvent.WaitOne(); + SchedThread.WaitEvent.WaitOne(); PrintDbgThreadInfo(Thread, "resuming execution..."); - - return Result; } private void RunThread(SchedulerThread SchedThread) { if (!SchedThread.Thread.Thread.Execute()) { - SchedThread.SchedWaitEvent.Set(); + SchedThread.WaitEvent.Set(); } else { @@ -368,21 +328,6 @@ namespace Ryujinx.Core.OsHle.Handles } } - private bool IsActive(KThread Thread) - { - if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) - { - throw new InvalidOperationException(); - } - - return IsActive(SchedThread); - } - - private bool IsActive(SchedulerThread SchedThread) - { - return SchedThread.Active && SchedThread.SyncTimeout == 0; - } - private void PrintDbgThreadInfo(KThread Thread, string Message) { Logging.Debug(LogClass.KernelScheduler, "(" + diff --git a/Ryujinx.Core/OsHle/Handles/KThread.cs b/Ryujinx.Core/OsHle/Handles/KThread.cs index 146c800ec2..9742f49220 100644 --- a/Ryujinx.Core/OsHle/Handles/KThread.cs +++ b/Ryujinx.Core/OsHle/Handles/KThread.cs @@ -6,19 +6,10 @@ namespace Ryujinx.Core.OsHle.Handles { public AThread Thread { get; private set; } - public KThread NextMutexThread { get; set; } - public KThread NextCondVarThread { get; set; } - - public long MutexAddress { get; set; } - public long CondVarAddress { get; set; } - public int ProcessorId { get; private set; } - public int Priority { get; private set; } - - private int DesiredPriority; - - public int Handle { get; set; } + public int Priority { get; set; } + public int Handle { get; set; } public int ThreadId => Thread.ThreadId; @@ -26,28 +17,7 @@ namespace Ryujinx.Core.OsHle.Handles { this.Thread = Thread; this.ProcessorId = ProcessorId; - - SetPriority(Priority); - } - - public void SetPriority(int Priority) - { - this.Priority = DesiredPriority = Priority; - - UpdatePriority(); - } - - public void ResetPriority() - { - Priority = DesiredPriority; - } - - public void UpdatePriority() - { - if (Priority > (NextMutexThread?.Priority ?? Priority)) - { - Priority = NextMutexThread.Priority; - } + this.Priority = Priority; } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs new file mode 100644 index 0000000000..f765737620 --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs @@ -0,0 +1,148 @@ +using Ryujinx.Core.OsHle.Handles; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Kernel +{ + class ConditionVariable + { + private Process Process; + + private long CondVarAddress; + + private bool OwnsCondVarValue; + + private List<(KThread Thread, AutoResetEvent WaitEvent)> WaitingThreads; + + public ConditionVariable(Process Process, long CondVarAddress) + { + this.Process = Process; + this.CondVarAddress = CondVarAddress; + + WaitingThreads = new List<(KThread, AutoResetEvent)>(); + } + + public bool WaitForSignal(KThread Thread, ulong Timeout) + { + bool Result = true; + + int Count = Process.Memory.ReadInt32(CondVarAddress); + + if (Count <= 0) + { + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) + { + lock (WaitingThreads) + { + WaitingThreads.Add((Thread, WaitEvent)); + } + + if (Timeout == ulong.MaxValue) + { + Result = WaitEvent.WaitOne(); + } + else + { + Result = WaitEvent.WaitOne(NsTimeConverter.GetTimeMs(Timeout)); + + lock (WaitingThreads) + { + WaitingThreads.Remove((Thread, WaitEvent)); + } + } + } + } + + AcquireCondVarValue(); + + Count = Process.Memory.ReadInt32(CondVarAddress); + + if (Result && Count > 0) + { + Process.Memory.WriteInt32(CondVarAddress, Count - 1); + } + + ReleaseCondVarValue(); + + return Result; + } + + public void SetSignal(KThread Thread, int Count) + { + lock (WaitingThreads) + { + if (Count < 0) + { + foreach ((_, AutoResetEvent WaitEvent) in WaitingThreads) + { + IncrementCondVarValue(); + + WaitEvent.Set(); + } + + WaitingThreads.Clear(); + } + else + { + while (WaitingThreads.Count > 0 && Count-- > 0) + { + int HighestPriority = WaitingThreads[0].Thread.Priority; + int HighestPrioIndex = 0; + + for (int Index = 1; Index < WaitingThreads.Count; Index++) + { + if (HighestPriority > WaitingThreads[Index].Thread.Priority) + { + HighestPriority = WaitingThreads[Index].Thread.Priority; + + HighestPrioIndex = Index; + } + } + + IncrementCondVarValue(); + + WaitingThreads[HighestPrioIndex].WaitEvent.Set(); + + WaitingThreads.RemoveAt(HighestPrioIndex); + } + } + } + + Process.Scheduler.Yield(Thread); + } + + private void IncrementCondVarValue() + { + AcquireCondVarValue(); + + int Count = Process.Memory.ReadInt32(CondVarAddress); + + Process.Memory.WriteInt32(CondVarAddress, Count + 1); + + ReleaseCondVarValue(); + } + + private void AcquireCondVarValue() + { + if (!OwnsCondVarValue) + { + while (!Process.Memory.AcquireAddress(CondVarAddress)) + { + Thread.Yield(); + } + + OwnsCondVarValue = true; + } + } + + private void ReleaseCondVarValue() + { + if (OwnsCondVarValue) + { + OwnsCondVarValue = false; + + Process.Memory.ReleaseAddress(CondVarAddress); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs index b568405b7c..e7cd72dc84 100644 --- a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs +++ b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs @@ -2,11 +2,9 @@ namespace Ryujinx.Core.OsHle.Kernel { static class KernelErr { - public const int InvalidAlignment = 102; - public const int InvalidAddress = 106; - public const int InvalidMemRange = 110; - public const int InvalidHandle = 114; - public const int Timeout = 117; - public const int InvalidInfo = 120; + public const int InvalidMemRange = 110; + public const int InvalidHandle = 114; + public const int Timeout = 117; + public const int InvalidInfo = 120; } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs b/Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs new file mode 100644 index 0000000000..9f05406b8f --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs @@ -0,0 +1,95 @@ +using Ryujinx.Core.OsHle.Handles; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Kernel +{ + class MutualExclusion + { + private const int MutexHasListenersMask = 0x40000000; + + private Process Process; + + private long MutexAddress; + + private int OwnerThreadHandle; + + private List<(KThread Thread, AutoResetEvent WaitEvent)> WaitingThreads; + + public MutualExclusion(Process Process, long MutexAddress) + { + this.Process = Process; + this.MutexAddress = MutexAddress; + + WaitingThreads = new List<(KThread, AutoResetEvent)>(); + } + + public void WaitForLock(KThread RequestingThread) + { + WaitForLock(RequestingThread, OwnerThreadHandle); + } + + public void WaitForLock(KThread RequestingThread, int OwnerThreadHandle) + { + if (OwnerThreadHandle == RequestingThread.Handle || + OwnerThreadHandle == 0) + { + return; + } + + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) + { + lock (WaitingThreads) + { + WaitingThreads.Add((RequestingThread, WaitEvent)); + } + + Process.Scheduler.Suspend(RequestingThread.ProcessorId); + + WaitEvent.WaitOne(); + + Process.Scheduler.Resume(RequestingThread); + } + } + + public void Unlock() + { + lock (WaitingThreads) + { + int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0; + + if (WaitingThreads.Count > 0) + { + int HighestPriority = WaitingThreads[0].Thread.Priority; + int HighestPrioIndex = 0; + + for (int Index = 1; Index < WaitingThreads.Count; Index++) + { + if (HighestPriority > WaitingThreads[Index].Thread.Priority) + { + HighestPriority = WaitingThreads[Index].Thread.Priority; + + HighestPrioIndex = Index; + } + } + + int Handle = WaitingThreads[HighestPrioIndex].Thread.Handle; + + WaitingThreads[HighestPrioIndex].WaitEvent.Set(); + + WaitingThreads.RemoveAt(HighestPrioIndex); + + Process.Memory.WriteInt32(MutexAddress, HasListeners | Handle); + + OwnerThreadHandle = Handle; + } + else + { + Process.Memory.WriteInt32(MutexAddress, 0); + + OwnerThreadHandle = 0; + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs index 015163939d..16ef86978a 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs @@ -3,6 +3,7 @@ using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.Core.OsHle.Handles; using System; +using System.Collections.Concurrent; using System.Collections.Generic; namespace Ryujinx.Core.OsHle.Kernel @@ -17,6 +18,9 @@ namespace Ryujinx.Core.OsHle.Kernel private Process Process; private AMemory Memory; + private ConcurrentDictionary Mutexes; + private ConcurrentDictionary CondVars; + private HashSet<(HSharedMem, long)> MappedSharedMems; private ulong CurrentHeapSize; @@ -67,6 +71,9 @@ namespace Ryujinx.Core.OsHle.Kernel this.Process = Process; this.Memory = Process.Memory; + Mutexes = new ConcurrentDictionary(); + CondVars = new ConcurrentDictionary(); + MappedSharedMems = new HashSet<(HSharedMem, long)>(); } diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs index 33092f259d..06147b28ff 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs @@ -110,7 +110,7 @@ namespace Ryujinx.Core.OsHle.Kernel if (CurrThread != null) { - CurrThread.SetPriority(Priority); + CurrThread.Priority = Priority; ThreadState.X0 = 0; } diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs index aad40c9948..38d759d35a 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs @@ -1,7 +1,5 @@ using ChocolArm64.State; using Ryujinx.Core.OsHle.Handles; -using System; -using System.Threading; using static Ryujinx.Core.OsHle.ErrorCode; @@ -9,32 +7,12 @@ namespace Ryujinx.Core.OsHle.Kernel { partial class SvcHandler { - private const int MutexHasListenersMask = 0x40000000; - private void SvcArbitrateLock(AThreadState ThreadState) { int OwnerThreadHandle = (int)ThreadState.X0; long MutexAddress = (long)ThreadState.X1; int RequestingThreadHandle = (int)ThreadState.X2; - if (IsPointingInsideKernel(MutexAddress)) - { - Logging.Warn(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - - return; - } - - if (IsWordAddressUnaligned(MutexAddress)) - { - Logging.Warn(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); - - return; - } - KThread OwnerThread = Process.HandleTable.GetData(OwnerThreadHandle); if (OwnerThread == null) @@ -57,7 +35,9 @@ namespace Ryujinx.Core.OsHle.Kernel return; } - MutexLock(MutexAddress, RequestingThread, OwnerThreadHandle); + MutualExclusion Mutex = GetMutex(MutexAddress); + + Mutex.WaitForLock(RequestingThread, OwnerThreadHandle); ThreadState.X0 = 0; } @@ -66,28 +46,9 @@ namespace Ryujinx.Core.OsHle.Kernel { long MutexAddress = (long)ThreadState.X0; - if (IsPointingInsideKernel(MutexAddress)) - { - Logging.Warn(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + GetMutex(MutexAddress).Unlock(); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - - return; - } - - if (IsWordAddressUnaligned(MutexAddress)) - { - Logging.Warn(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); - - return; - } - - if (MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress)) - { - Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr)); - } + Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr)); ThreadState.X0 = 0; } @@ -99,24 +60,6 @@ namespace Ryujinx.Core.OsHle.Kernel int ThreadHandle = (int)ThreadState.X2; ulong Timeout = ThreadState.X3; - if (IsPointingInsideKernel(MutexAddress)) - { - Logging.Warn(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - - return; - } - - if (IsWordAddressUnaligned(MutexAddress)) - { - Logging.Warn(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); - - return; - } - KThread Thread = Process.HandleTable.GetData(ThreadHandle); if (Thread == null) @@ -124,20 +67,24 @@ namespace Ryujinx.Core.OsHle.Kernel Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; } - MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress); + Process.Scheduler.Suspend(Thread.ProcessorId); - if (!CondVarWait(Thread, MutexAddress, CondVarAddress, Timeout)) + MutualExclusion Mutex = GetMutex(MutexAddress); + + Mutex.Unlock(); + + if (!GetCondVar(CondVarAddress).WaitForSignal(Thread, Timeout)) { ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); return; } - Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr)); + Mutex.WaitForLock(Thread); + + Process.Scheduler.Resume(Thread); ThreadState.X0 = 0; } @@ -147,227 +94,31 @@ namespace Ryujinx.Core.OsHle.Kernel long CondVarAddress = (long)ThreadState.X0; int Count = (int)ThreadState.X1; - CondVarSignal(CondVarAddress, Count); + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + GetCondVar(CondVarAddress).SetSignal(CurrThread, Count); ThreadState.X0 = 0; } - private void MutexLock(long MutexAddress, KThread RequestingThread, int OwnerThreadHandle) + private MutualExclusion GetMutex(long MutexAddress) { - int MutexValue = Process.Memory.ReadInt32(MutexAddress); - - if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask)) + MutualExclusion MutexFactory(long Key) { - return; + return new MutualExclusion(Process, MutexAddress); } - InsertWaitingMutexThread(OwnerThreadHandle, RequestingThread); - - RequestingThread.MutexAddress = MutexAddress; - - Process.Scheduler.EnterWait(RequestingThread); + return Mutexes.GetOrAdd(MutexAddress, MutexFactory); } - private bool MutexUnlock(KThread CurrThread, long MutexAddress) + private ConditionVariable GetCondVar(long CondVarAddress) { - if (CurrThread == null) + ConditionVariable CondVarFactory(long Key) { - Logging.Warn(LogClass.KernelSvc, $"Invalid mutex 0x{MutexAddress:x16}!"); - - return false; + return new ConditionVariable(Process, CondVarAddress); } - CurrThread.MutexAddress = 0; - CurrThread.CondVarAddress = 0; - - CurrThread.ResetPriority(); - - KThread OwnerThread = CurrThread.NextMutexThread; - - CurrThread.NextMutexThread = null; - - if (OwnerThread != null) - { - int HasListeners = OwnerThread.NextMutexThread != null ? MutexHasListenersMask : 0; - - Process.Memory.WriteInt32(MutexAddress, HasListeners | OwnerThread.Handle); - - Process.Scheduler.WakeUp(OwnerThread); - - return true; - } - else - { - Process.Memory.WriteInt32(MutexAddress, 0); - - return false; - } - } - - private bool CondVarWait(KThread WaitThread, long MutexAddress, long CondVarAddress, ulong Timeout) - { - KThread CurrThread = Process.ThreadArbiterList; - - if (CurrThread != null) - { - bool DoInsert = CurrThread != WaitThread; - - while (CurrThread.NextCondVarThread != null) - { - if (CurrThread.NextCondVarThread.Priority < WaitThread.Priority) - { - break; - } - - CurrThread = CurrThread.NextCondVarThread; - - DoInsert &= CurrThread != WaitThread; - } - - if (DoInsert) - { - if (WaitThread.NextCondVarThread != null) - { - throw new InvalidOperationException(); - } - - WaitThread.NextCondVarThread = CurrThread.NextCondVarThread; - CurrThread.NextCondVarThread = WaitThread; - } - - CurrThread.MutexAddress = MutexAddress; - CurrThread.CondVarAddress = CondVarAddress; - } - else - { - Process.ThreadArbiterList = WaitThread; - } - - if (Timeout != ulong.MaxValue) - { - return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout)); - } - else - { - return Process.Scheduler.EnterWait(WaitThread); - } - } - - private void CondVarSignal(long CondVarAddress, int Count) - { - KThread PrevThread = null; - KThread CurrThread = Process.ThreadArbiterList; - - while (CurrThread != null && (Count == -1 || Count-- > 0)) - { - if (CurrThread.CondVarAddress == CondVarAddress) - { - if (PrevThread != null) - { - PrevThread.NextCondVarThread = CurrThread.NextCondVarThread; - } - else - { - Process.ThreadArbiterList = CurrThread.NextCondVarThread; - } - - CurrThread.NextCondVarThread = null; - - AcquireMutexValue(CurrThread.MutexAddress); - - int MutexValue = Process.Memory.ReadInt32(CurrThread.MutexAddress); - - MutexValue &= ~MutexHasListenersMask; - - if (MutexValue == 0) - { - Process.Memory.WriteInt32(CurrThread.MutexAddress, CurrThread.Handle); - - CurrThread.MutexAddress = 0; - CurrThread.CondVarAddress = 0; - - CurrThread.ResetPriority(); - - Process.Scheduler.WakeUp(CurrThread); - } - else - { - InsertWaitingMutexThread(MutexValue, CurrThread); - - MutexValue |= MutexHasListenersMask; - - Process.Memory.WriteInt32(CurrThread.MutexAddress, MutexValue); - } - - ReleaseMutexValue(CurrThread.MutexAddress); - } - - PrevThread = CurrThread; - CurrThread = CurrThread.NextCondVarThread; - } - } - - private void InsertWaitingMutexThread(int OwnerThreadHandle, KThread WaitThread) - { - KThread CurrThread = Process.HandleTable.GetData(OwnerThreadHandle); - - if (CurrThread == null) - { - Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!"); - - return; - } - - while (CurrThread.NextMutexThread != null) - { - if (CurrThread == WaitThread) - { - return; - } - - if (CurrThread.NextMutexThread.Priority < WaitThread.Priority) - { - break; - } - - CurrThread = CurrThread.NextMutexThread; - } - - if (CurrThread != WaitThread) - { - if (WaitThread.NextCondVarThread != null) - { - throw new InvalidOperationException(); - } - - WaitThread.NextMutexThread = CurrThread.NextMutexThread; - CurrThread.NextMutexThread = WaitThread; - - CurrThread.UpdatePriority(); - } - } - - private void AcquireMutexValue(long MutexAddress) - { - while (!Process.Memory.AcquireAddress(MutexAddress)) - { - Thread.Yield(); - } - } - - private void ReleaseMutexValue(long MutexAddress) - { - Process.Memory.ReleaseAddress(MutexAddress); - } - - private bool IsPointingInsideKernel(long Address) - { - return ((ulong)Address + 0x1000000000) < 0xffffff000; - } - - private bool IsWordAddressUnaligned(long Address) - { - return (Address & 3) != 0; + return CondVars.GetOrAdd(CondVarAddress, CondVarFactory); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index 06a7fcf8c3..bacca9a397 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -10,7 +10,6 @@ using Ryujinx.Core.OsHle.Services.Nv; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; namespace Ryujinx.Core.OsHle { @@ -36,8 +35,6 @@ namespace Ryujinx.Core.OsHle public KProcessScheduler Scheduler { get; private set; } - public KThread ThreadArbiterList { get; set; } - public KProcessHandleTable HandleTable { get; private set; } public AppletStateMgr AppletState { get; private set; } @@ -46,7 +43,7 @@ namespace Ryujinx.Core.OsHle private ConcurrentDictionary TlsSlots; - private ConcurrentDictionary Threads; + private ConcurrentDictionary ThreadsByTpidr; private List Executables; @@ -74,7 +71,7 @@ namespace Ryujinx.Core.OsHle TlsSlots = new ConcurrentDictionary(); - Threads = new ConcurrentDictionary(); + ThreadsByTpidr = new ConcurrentDictionary(); Executables = new List(); @@ -188,34 +185,34 @@ namespace Ryujinx.Core.OsHle throw new ObjectDisposedException(nameof(Process)); } - AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint); + AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint); - KThread Thread = new KThread(CpuThread, ProcessorId, Priority); + KThread KernelThread = new KThread(Thread, ProcessorId, Priority); - int Handle = HandleTable.OpenHandle(Thread); + int Handle = HandleTable.OpenHandle(KernelThread); - Thread.Handle = Handle; + KernelThread.Handle = Handle; - int ThreadId = GetFreeTlsSlot(CpuThread); + int ThreadId = GetFreeTlsSlot(Thread); long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize; - CpuThread.ThreadState.ProcessId = ProcessId; - CpuThread.ThreadState.ThreadId = ThreadId; - CpuThread.ThreadState.CntfrqEl0 = TickFreq; - CpuThread.ThreadState.Tpidr = Tpidr; + Thread.ThreadState.ProcessId = ProcessId; + Thread.ThreadState.ThreadId = ThreadId; + Thread.ThreadState.CntfrqEl0 = TickFreq; + Thread.ThreadState.Tpidr = Tpidr; - CpuThread.ThreadState.X0 = (ulong)ArgsPtr; - CpuThread.ThreadState.X1 = (ulong)Handle; - CpuThread.ThreadState.X31 = (ulong)StackTop; + Thread.ThreadState.X0 = (ulong)ArgsPtr; + Thread.ThreadState.X1 = (ulong)Handle; + Thread.ThreadState.X31 = (ulong)StackTop; - CpuThread.ThreadState.Break += BreakHandler; - CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall; - CpuThread.ThreadState.Undefined += UndefinedHandler; + Thread.ThreadState.Break += BreakHandler; + Thread.ThreadState.SvcCall += SvcHandler.SvcCall; + Thread.ThreadState.Undefined += UndefinedHandler; - CpuThread.WorkFinished += ThreadFinished; + Thread.WorkFinished += ThreadFinished; - Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread); + ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, KernelThread); return Handle; } @@ -327,7 +324,7 @@ namespace Ryujinx.Core.OsHle public KThread GetThread(long Tpidr) { - if (!Threads.TryGetValue(Tpidr, out KThread Thread)) + if (!ThreadsByTpidr.TryGetValue(Tpidr, out KThread Thread)) { Logging.Error(LogClass.KernelScheduler, $"Thread with TPIDR 0x{Tpidr:x16} not found!"); } @@ -335,28 +332,6 @@ namespace Ryujinx.Core.OsHle return Thread; } - public IEnumerable EnumerateThreadsWithMutex(long MutexAddress) - { - foreach (KThread Thread in Threads.Values.OrderBy(x => x.Priority)) - { - if (Thread.MutexAddress == MutexAddress) - { - yield return Thread; - } - } - } - - public IEnumerable EnumerateThreadsWithCondVar(long CondVarAddress) - { - foreach (KThread Thread in Threads.Values.OrderBy(x => x.Priority)) - { - if (Thread.CondVarAddress == CondVarAddress) - { - yield return Thread; - } - } - } - public void Dispose() { Dispose(true);