diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs')
-rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs new file mode 100644 index 00000000..9c196810 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs @@ -0,0 +1,142 @@ +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.Horizon.Common; +using System; +using System.Buffers; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Kernel.Threading +{ + class KSynchronization + { + private KernelContext _context; + + public KSynchronization(KernelContext context) + { + _context = context; + } + + public Result WaitFor(Span<KSynchronizationObject> syncObjs, long timeout, out int handleIndex) + { + handleIndex = 0; + + Result result = KernelResult.TimedOut; + + _context.CriticalSection.Enter(); + + // Check if objects are already signaled before waiting. + for (int index = 0; index < syncObjs.Length; index++) + { + if (!syncObjs[index].IsSignaled()) + { + continue; + } + + handleIndex = index; + + _context.CriticalSection.Leave(); + + return Result.Success; + } + + if (timeout == 0) + { + _context.CriticalSection.Leave(); + + return result; + } + + KThread currentThread = KernelStatic.GetCurrentThread(); + + if (currentThread.TerminationRequested) + { + result = KernelResult.ThreadTerminating; + } + else if (currentThread.SyncCancelled) + { + currentThread.SyncCancelled = false; + + result = KernelResult.Cancelled; + } + else + { + LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length); + + Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length); + + for (int index = 0; index < syncObjs.Length; index++) + { + syncNodes[index] = syncObjs[index].AddWaitingThread(currentThread); + } + + currentThread.WaitingSync = true; + currentThread.SignaledObj = null; + currentThread.ObjSyncResult = result; + + currentThread.Reschedule(ThreadSchedState.Paused); + + if (timeout > 0) + { + _context.TimeManager.ScheduleFutureInvocation(currentThread, timeout); + } + + _context.CriticalSection.Leave(); + + currentThread.WaitingSync = false; + + if (timeout > 0) + { + _context.TimeManager.UnscheduleFutureInvocation(currentThread); + } + + _context.CriticalSection.Enter(); + + result = currentThread.ObjSyncResult; + + handleIndex = -1; + + for (int index = 0; index < syncObjs.Length; index++) + { + syncObjs[index].RemoveWaitingThread(syncNodes[index]); + + if (syncObjs[index] == currentThread.SignaledObj) + { + handleIndex = index; + } + } + + ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray); + } + + _context.CriticalSection.Leave(); + + return result; + } + + public void SignalObject(KSynchronizationObject syncObj) + { + _context.CriticalSection.Enter(); + + if (syncObj.IsSignaled()) + { + LinkedListNode<KThread> node = syncObj.WaitingThreads.First; + + while (node != null) + { + KThread thread = node.Value; + + if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) + { + thread.SignaledObj = syncObj; + thread.ObjSyncResult = Result.Success; + + thread.Reschedule(ThreadSchedState.Running); + } + + node = node.Next; + } + } + + _context.CriticalSection.Leave(); + } + } +}
\ No newline at end of file |