Ryujinx/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs

148 lines
4.1 KiB
C#

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);
}
}
}
}