mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-09-11 10:18:52 +02:00
250 lines
7.1 KiB
C#
250 lines
7.1 KiB
C#
using Ryujinx.Common;
|
|
using Ryujinx.Horizon.Common;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
|
{
|
|
class MultiWaitImpl
|
|
{
|
|
private const int WaitTimedOut = -1;
|
|
private const int WaitCancelled = -2;
|
|
private const int WaitInvalid = -3;
|
|
|
|
private readonly List<MultiWaitHolderBase> _multiWaits;
|
|
|
|
private object _lock;
|
|
|
|
private int _waitingThreadHandle;
|
|
|
|
private MultiWaitHolderBase _signaledHolder;
|
|
|
|
public long CurrentTime { get; private set; }
|
|
|
|
public MultiWaitImpl()
|
|
{
|
|
_multiWaits = new List<MultiWaitHolderBase>();
|
|
|
|
_lock = new object();
|
|
}
|
|
|
|
public void LinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder)
|
|
{
|
|
_multiWaits.Add(multiWaitHolder);
|
|
}
|
|
|
|
public void UnlinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder)
|
|
{
|
|
_multiWaits.Remove(multiWaitHolder);
|
|
}
|
|
|
|
public void MoveAllFrom(MultiWaitImpl other)
|
|
{
|
|
foreach (MultiWaitHolderBase multiWait in other._multiWaits)
|
|
{
|
|
multiWait.SetMultiWait(this);
|
|
}
|
|
|
|
_multiWaits.AddRange(other._multiWaits);
|
|
|
|
other._multiWaits.Clear();
|
|
}
|
|
|
|
public MultiWaitHolderBase WaitAnyImpl(bool infinite, long timeout)
|
|
{
|
|
_signaledHolder = null;
|
|
_waitingThreadHandle = Os.GetCurrentThreadHandle();
|
|
|
|
MultiWaitHolderBase result = LinkHoldersToObjectList();
|
|
|
|
lock (_lock)
|
|
{
|
|
if (_signaledHolder != null)
|
|
{
|
|
result = _signaledHolder;
|
|
}
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
result = WaitAnyHandleImpl(infinite, timeout);
|
|
}
|
|
|
|
UnlinkHoldersFromObjectsList();
|
|
_waitingThreadHandle = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout)
|
|
{
|
|
Span<int> objectHandles = new int[64];
|
|
|
|
Span<MultiWaitHolderBase> objects = new MultiWaitHolderBase[64];
|
|
|
|
int count = FillObjectsArray(objectHandles, objects);
|
|
|
|
long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000;
|
|
|
|
while (true)
|
|
{
|
|
CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000;
|
|
|
|
MultiWaitHolderBase minTimeoutObject = RecalcMultiWaitTimeout(endTime, out long minTimeout);
|
|
|
|
int index;
|
|
|
|
if (count == 0 && minTimeout == 0)
|
|
{
|
|
index = WaitTimedOut;
|
|
}
|
|
else
|
|
{
|
|
index = WaitSynchronization(objectHandles.Slice(0, count), minTimeout);
|
|
|
|
DebugUtil.Assert(index != WaitInvalid);
|
|
}
|
|
|
|
switch (index)
|
|
{
|
|
case WaitTimedOut:
|
|
if (minTimeoutObject != null)
|
|
{
|
|
CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000;
|
|
|
|
if (minTimeoutObject.Signaled == TriBool.True)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_signaledHolder = minTimeoutObject;
|
|
|
|
return _signaledHolder;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
break;
|
|
case WaitCancelled:
|
|
lock (_lock)
|
|
{
|
|
if (_signaledHolder != null)
|
|
{
|
|
return _signaledHolder;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
lock (_lock)
|
|
{
|
|
_signaledHolder = objects[index];
|
|
|
|
return _signaledHolder;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private int FillObjectsArray(Span<int> handles, Span<MultiWaitHolderBase> objects)
|
|
{
|
|
int count = 0;
|
|
|
|
foreach (MultiWaitHolderBase holder in _multiWaits)
|
|
{
|
|
int handle = holder.Handle;
|
|
|
|
if (handle != 0)
|
|
{
|
|
handles[count] = handle;
|
|
objects[count] = holder;
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private MultiWaitHolderBase RecalcMultiWaitTimeout(long endTime, out long minTimeout)
|
|
{
|
|
MultiWaitHolderBase minTimeHolder = null;
|
|
|
|
long minTime = endTime;
|
|
|
|
foreach (MultiWaitHolder holder in _multiWaits)
|
|
{
|
|
long currentTime = holder.GetAbsoluteTimeToWakeup();
|
|
|
|
if ((ulong)currentTime < (ulong)minTime)
|
|
{
|
|
minTimeHolder = holder;
|
|
|
|
minTime = currentTime;
|
|
}
|
|
}
|
|
|
|
minTimeout = (ulong)minTime < (ulong)CurrentTime ? 0 : minTime - CurrentTime;
|
|
|
|
return minTimeHolder;
|
|
}
|
|
|
|
private static int WaitSynchronization(ReadOnlySpan<int> handles, long timeout)
|
|
{
|
|
Result result = HorizonStatic.Syscall.WaitSynchronization(out int index, handles, timeout);
|
|
|
|
if (result == KernelResult.TimedOut)
|
|
{
|
|
return WaitTimedOut;
|
|
}
|
|
else if (result == KernelResult.Cancelled)
|
|
{
|
|
return WaitCancelled;
|
|
}
|
|
else
|
|
{
|
|
result.AbortOnFailure();
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
public void NotifyAndWakeUpThread(MultiWaitHolderBase holder)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_signaledHolder == null)
|
|
{
|
|
_signaledHolder = holder;
|
|
HorizonStatic.Syscall.CancelSynchronization(_waitingThreadHandle).AbortOnFailure();
|
|
}
|
|
}
|
|
}
|
|
|
|
private MultiWaitHolderBase LinkHoldersToObjectList()
|
|
{
|
|
MultiWaitHolderBase signaledHolder = null;
|
|
|
|
foreach (MultiWaitHolderBase holder in _multiWaits)
|
|
{
|
|
TriBool isSignaled = holder.LinkToObjectList();
|
|
|
|
if (signaledHolder == null && isSignaled == TriBool.True)
|
|
{
|
|
signaledHolder = holder;
|
|
}
|
|
}
|
|
|
|
return signaledHolder;
|
|
}
|
|
|
|
private void UnlinkHoldersFromObjectsList()
|
|
{
|
|
foreach (MultiWaitHolderBase holder in _multiWaits)
|
|
{
|
|
holder.UnlinkFromObjectList();
|
|
}
|
|
}
|
|
}
|
|
}
|