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

148 lines
4.1 KiB
C#
Raw Normal View History

using Ryujinx.Core.OsHle.Handles;
using System.Collections.Generic;
2018-02-19 20:37:13 +01:00
using System.Threading;
2018-02-05 00:08:20 +01:00
namespace Ryujinx.Core.OsHle.Kernel
2018-02-05 00:08:20 +01:00
{
class ConditionVariable
2018-02-05 00:08:20 +01:00
{
private Process Process;
2018-02-05 00:08:20 +01:00
private long CondVarAddress;
2018-02-19 20:37:13 +01:00
private bool OwnsCondVarValue;
private List<(KThread Thread, AutoResetEvent WaitEvent)> WaitingThreads;
2018-02-05 00:08:20 +01:00
public ConditionVariable(Process Process, long CondVarAddress)
2018-02-05 00:08:20 +01:00
{
this.Process = Process;
2018-02-05 00:08:20 +01:00
this.CondVarAddress = CondVarAddress;
WaitingThreads = new List<(KThread, AutoResetEvent)>();
2018-02-05 00:08:20 +01:00
}
public bool WaitForSignal(KThread Thread, ulong Timeout)
2018-02-05 00:08:20 +01:00
{
bool Result = true;
2018-02-19 20:37:13 +01:00
int Count = Process.Memory.ReadInt32(CondVarAddress);
2018-02-05 00:08:20 +01:00
if (Count <= 0)
{
using (AutoResetEvent WaitEvent = new AutoResetEvent(false))
2018-02-19 20:37:13 +01:00
{
lock (WaitingThreads)
{
WaitingThreads.Add((Thread, WaitEvent));
}
2018-02-19 20:37:13 +01:00
if (Timeout == ulong.MaxValue)
2018-02-19 20:37:13 +01:00
{
Result = WaitEvent.WaitOne();
}
else
{
Result = WaitEvent.WaitOne(NsTimeConverter.GetTimeMs(Timeout));
lock (WaitingThreads)
{
WaitingThreads.Remove((Thread, WaitEvent));
}
2018-02-19 20:37:13 +01:00
}
}
2018-02-05 00:08:20 +01:00
}
2018-02-19 20:37:13 +01:00
AcquireCondVarValue();
2018-02-05 00:08:20 +01:00
2018-02-19 20:37:13 +01:00
Count = Process.Memory.ReadInt32(CondVarAddress);
2018-02-05 00:08:20 +01:00
if (Result && Count > 0)
2018-02-05 00:08:20 +01:00
{
2018-02-19 20:37:13 +01:00
Process.Memory.WriteInt32(CondVarAddress, Count - 1);
2018-02-05 00:08:20 +01:00
}
2018-02-19 20:37:13 +01:00
ReleaseCondVarValue();
return Result;
2018-02-05 00:08:20 +01:00
}
public void SetSignal(KThread Thread, int Count)
2018-02-05 00:08:20 +01:00
{
lock (WaitingThreads)
2018-02-05 00:08:20 +01:00
{
if (Count < 0)
2018-02-05 00:08:20 +01:00
{
foreach ((_, AutoResetEvent WaitEvent) in WaitingThreads)
{
IncrementCondVarValue();
WaitEvent.Set();
}
WaitingThreads.Clear();
2018-02-05 00:08:20 +01:00
}
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);
}
}
2018-02-05 00:08:20 +01:00
}
Process.Scheduler.Yield(Thread);
2018-02-05 00:08:20 +01:00
}
private void IncrementCondVarValue()
{
AcquireCondVarValue();
int Count = Process.Memory.ReadInt32(CondVarAddress);
Process.Memory.WriteInt32(CondVarAddress, Count + 1);
ReleaseCondVarValue();
}
2018-02-19 20:37:13 +01:00
private void AcquireCondVarValue()
{
2018-02-19 20:37:13 +01:00
if (!OwnsCondVarValue)
{
while (!Process.Memory.AcquireAddress(CondVarAddress))
{
Thread.Yield();
}
OwnsCondVarValue = true;
}
}
2018-02-19 20:37:13 +01:00
private void ReleaseCondVarValue()
{
2018-02-19 20:37:13 +01:00
if (OwnsCondVarValue)
{
OwnsCondVarValue = false;
Process.Memory.ReleaseAddress(CondVarAddress);
}
}
2018-02-05 00:08:20 +01:00
}
}