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 _multiWaits; private object _lock; private int _waitingThreadHandle; private MultiWaitHolderBase _signaledHolder; public long CurrentTime { get; private set; } public MultiWaitImpl() { _multiWaits = new List(); _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 objectHandles = new int[64]; Span 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 handles, Span 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 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(); } } } }