aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs')
-rw-r--r--Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs139
1 files changed, 139 insertions, 0 deletions
diff --git a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs
new file mode 100644
index 00000000..34d5820b
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs
@@ -0,0 +1,139 @@
+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, long 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));
+ }
+
+ Process.Scheduler.Suspend(Thread.ProcessorId);
+
+ if (Timeout < 0)
+ {
+ Result = WaitEvent.WaitOne();
+ }
+ else
+ {
+ Result = WaitEvent.WaitOne((int)(Timeout / 1000000));
+
+ lock (WaitingThreads)
+ {
+ WaitingThreads.Remove((Thread, WaitEvent));
+ }
+ }
+
+ Process.Scheduler.Resume(Thread);
+ }
+ }
+
+ AcquireCondVarValue();
+
+ Count = Process.Memory.ReadInt32(CondVarAddress);
+
+ if (Count > 0)
+ {
+ Process.Memory.WriteInt32(CondVarAddress, Count - 1);
+ }
+
+ ReleaseCondVarValue();
+
+ return Result;
+ }
+
+ public void SetSignal(KThread Thread, int Count)
+ {
+ lock (WaitingThreads)
+ {
+ if (Count < 0)
+ {
+ Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
+
+ foreach ((_, AutoResetEvent WaitEvent) in WaitingThreads)
+ {
+ WaitEvent.Set();
+ }
+
+ WaitingThreads.Clear();
+ }
+ else
+ {
+ Process.Memory.WriteInt32(CondVarAddress, Count);
+
+ 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;
+ }
+ }
+
+ WaitingThreads[HighestPrioIndex].WaitEvent.Set();
+
+ WaitingThreads.RemoveAt(HighestPrioIndex);
+ }
+ }
+ }
+ }
+
+ 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