diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Threading')
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs | 9 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs | 581 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs | 70 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs | 64 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs | 14 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs | 286 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs | 65 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs | 661 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs | 142 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 1438 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs | 33 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs | 25 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs | 9 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs | 20 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs | 10 |
15 files changed, 0 insertions, 3427 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs b/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs deleted file mode 100644 index 89c1bf1f..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/ArbitrationType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - enum ArbitrationType - { - WaitIfLessThan = 0, - DecrementAndWaitIfLessThan = 1, - WaitIfEqual = 2 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs deleted file mode 100644 index 74867b44..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ /dev/null @@ -1,581 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.Horizon.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KAddressArbiter - { - private const int HasListenersMask = 0x40000000; - - private readonly KernelContext _context; - - private readonly List<KThread> _condVarThreads; - private readonly List<KThread> _arbiterThreads; - - public KAddressArbiter(KernelContext context) - { - _context = context; - - _condVarThreads = new List<KThread>(); - _arbiterThreads = new List<KThread>(); - } - - public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - _context.CriticalSection.Enter(); - - if (currentThread.TerminationRequested) - { - _context.CriticalSection.Leave(); - - return KernelResult.ThreadTerminating; - } - - currentThread.SignaledObj = null; - currentThread.ObjSyncResult = Result.Success; - - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - - if (!KernelTransfer.UserToKernel(out int mutexValue, mutexAddress)) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - if (mutexValue != (ownerHandle | HasListenersMask)) - { - _context.CriticalSection.Leave(); - - return Result.Success; - } - - KThread mutexOwner = currentProcess.HandleTable.GetObject<KThread>(ownerHandle); - - if (mutexOwner == null) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidHandle; - } - - currentThread.MutexAddress = mutexAddress; - currentThread.ThreadHandleForUserMutex = requesterHandle; - - mutexOwner.AddMutexWaiter(currentThread); - - currentThread.Reschedule(ThreadSchedState.Paused); - - _context.CriticalSection.Leave(); - _context.CriticalSection.Enter(); - - if (currentThread.MutexOwner != null) - { - currentThread.MutexOwner.RemoveMutexWaiter(currentThread); - } - - _context.CriticalSection.Leave(); - - return currentThread.ObjSyncResult; - } - - public Result ArbitrateUnlock(ulong mutexAddress) - { - _context.CriticalSection.Enter(); - - KThread currentThread = KernelStatic.GetCurrentThread(); - - (int mutexValue, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress); - - Result result = Result.Success; - - if (!KernelTransfer.KernelToUser(mutexAddress, mutexValue)) - { - result = KernelResult.InvalidMemState; - } - - if (result != Result.Success && newOwnerThread != null) - { - newOwnerThread.SignaledObj = null; - newOwnerThread.ObjSyncResult = result; - } - - _context.CriticalSection.Leave(); - - return result; - } - - public Result WaitProcessWideKeyAtomic(ulong mutexAddress, ulong condVarAddress, int threadHandle, long timeout) - { - _context.CriticalSection.Enter(); - - KThread currentThread = KernelStatic.GetCurrentThread(); - - currentThread.SignaledObj = null; - currentThread.ObjSyncResult = KernelResult.TimedOut; - - if (currentThread.TerminationRequested) - { - _context.CriticalSection.Leave(); - - return KernelResult.ThreadTerminating; - } - - (int mutexValue, _) = MutexUnlock(currentThread, mutexAddress); - - KernelTransfer.KernelToUser(condVarAddress, 1); - - if (!KernelTransfer.KernelToUser(mutexAddress, mutexValue)) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - currentThread.MutexAddress = mutexAddress; - currentThread.ThreadHandleForUserMutex = threadHandle; - currentThread.CondVarAddress = condVarAddress; - - _condVarThreads.Add(currentThread); - - if (timeout != 0) - { - currentThread.Reschedule(ThreadSchedState.Paused); - - if (timeout > 0) - { - _context.TimeManager.ScheduleFutureInvocation(currentThread, timeout); - } - } - - _context.CriticalSection.Leave(); - - if (timeout > 0) - { - _context.TimeManager.UnscheduleFutureInvocation(currentThread); - } - - _context.CriticalSection.Enter(); - - if (currentThread.MutexOwner != null) - { - currentThread.MutexOwner.RemoveMutexWaiter(currentThread); - } - - _condVarThreads.Remove(currentThread); - - _context.CriticalSection.Leave(); - - return currentThread.ObjSyncResult; - } - - private (int, KThread) MutexUnlock(KThread currentThread, ulong mutexAddress) - { - KThread newOwnerThread = currentThread.RelinquishMutex(mutexAddress, out int count); - - int mutexValue = 0; - - if (newOwnerThread != null) - { - mutexValue = newOwnerThread.ThreadHandleForUserMutex; - - if (count >= 2) - { - mutexValue |= HasListenersMask; - } - - newOwnerThread.SignaledObj = null; - newOwnerThread.ObjSyncResult = Result.Success; - - newOwnerThread.ReleaseAndResume(); - } - - return (mutexValue, newOwnerThread); - } - - public void SignalProcessWideKey(ulong address, int count) - { - _context.CriticalSection.Enter(); - - WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address); - - if (!_condVarThreads.Any(x => x.CondVarAddress == address)) - { - KernelTransfer.KernelToUser(address, 0); - } - - _context.CriticalSection.Leave(); - } - - private static void TryAcquireMutex(KThread requester) - { - ulong address = requester.MutexAddress; - - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - - if (!currentProcess.CpuMemory.IsMapped(address)) - { - // Invalid address. - requester.SignaledObj = null; - requester.ObjSyncResult = KernelResult.InvalidMemState; - - return; - } - - ref int mutexRef = ref currentProcess.CpuMemory.GetRef<int>(address); - - int mutexValue, newMutexValue; - - do - { - mutexValue = mutexRef; - - if (mutexValue != 0) - { - // Update value to indicate there is a mutex waiter now. - newMutexValue = mutexValue | HasListenersMask; - } - else - { - // No thread owning the mutex, assign to requesting thread. - newMutexValue = requester.ThreadHandleForUserMutex; - } - } - while (Interlocked.CompareExchange(ref mutexRef, newMutexValue, mutexValue) != mutexValue); - - if (mutexValue == 0) - { - // We now own the mutex. - requester.SignaledObj = null; - requester.ObjSyncResult = Result.Success; - - requester.ReleaseAndResume(); - - return; - } - - mutexValue &= ~HasListenersMask; - - KThread mutexOwner = currentProcess.HandleTable.GetObject<KThread>(mutexValue); - - if (mutexOwner != null) - { - // Mutex already belongs to another thread, wait for it. - mutexOwner.AddMutexWaiter(requester); - } - else - { - // Invalid mutex owner. - requester.SignaledObj = null; - requester.ObjSyncResult = KernelResult.InvalidHandle; - - requester.ReleaseAndResume(); - } - } - - public Result WaitForAddressIfEqual(ulong address, int value, long timeout) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - _context.CriticalSection.Enter(); - - if (currentThread.TerminationRequested) - { - _context.CriticalSection.Leave(); - - return KernelResult.ThreadTerminating; - } - - currentThread.SignaledObj = null; - currentThread.ObjSyncResult = KernelResult.TimedOut; - - if (!KernelTransfer.UserToKernel(out int currentValue, address)) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - if (currentValue == value) - { - if (timeout == 0) - { - _context.CriticalSection.Leave(); - - return KernelResult.TimedOut; - } - - currentThread.MutexAddress = address; - currentThread.WaitingInArbitration = true; - - _arbiterThreads.Add(currentThread); - - currentThread.Reschedule(ThreadSchedState.Paused); - - if (timeout > 0) - { - _context.TimeManager.ScheduleFutureInvocation(currentThread, timeout); - } - - _context.CriticalSection.Leave(); - - if (timeout > 0) - { - _context.TimeManager.UnscheduleFutureInvocation(currentThread); - } - - _context.CriticalSection.Enter(); - - if (currentThread.WaitingInArbitration) - { - _arbiterThreads.Remove(currentThread); - - currentThread.WaitingInArbitration = false; - } - - _context.CriticalSection.Leave(); - - return currentThread.ObjSyncResult; - } - - _context.CriticalSection.Leave(); - - return KernelResult.InvalidState; - } - - public Result WaitForAddressIfLessThan(ulong address, int value, bool shouldDecrement, long timeout) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - _context.CriticalSection.Enter(); - - if (currentThread.TerminationRequested) - { - _context.CriticalSection.Leave(); - - return KernelResult.ThreadTerminating; - } - - currentThread.SignaledObj = null; - currentThread.ObjSyncResult = KernelResult.TimedOut; - - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - - if (!KernelTransfer.UserToKernel(out int currentValue, address)) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - if (shouldDecrement) - { - currentValue = Interlocked.Decrement(ref currentProcess.CpuMemory.GetRef<int>(address)) + 1; - } - - if (currentValue < value) - { - if (timeout == 0) - { - _context.CriticalSection.Leave(); - - return KernelResult.TimedOut; - } - - currentThread.MutexAddress = address; - currentThread.WaitingInArbitration = true; - - _arbiterThreads.Add(currentThread); - - currentThread.Reschedule(ThreadSchedState.Paused); - - if (timeout > 0) - { - _context.TimeManager.ScheduleFutureInvocation(currentThread, timeout); - } - - _context.CriticalSection.Leave(); - - if (timeout > 0) - { - _context.TimeManager.UnscheduleFutureInvocation(currentThread); - } - - _context.CriticalSection.Enter(); - - if (currentThread.WaitingInArbitration) - { - _arbiterThreads.Remove(currentThread); - - currentThread.WaitingInArbitration = false; - } - - _context.CriticalSection.Leave(); - - return currentThread.ObjSyncResult; - } - - _context.CriticalSection.Leave(); - - return KernelResult.InvalidState; - } - - public Result Signal(ulong address, int count) - { - _context.CriticalSection.Enter(); - - WakeArbiterThreads(address, count); - - _context.CriticalSection.Leave(); - - return Result.Success; - } - - public Result SignalAndIncrementIfEqual(ulong address, int value, int count) - { - _context.CriticalSection.Enter(); - - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - - if (!currentProcess.CpuMemory.IsMapped(address)) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - ref int valueRef = ref currentProcess.CpuMemory.GetRef<int>(address); - - int currentValue; - - do - { - currentValue = valueRef; - - if (currentValue != value) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidState; - } - } - while (Interlocked.CompareExchange(ref valueRef, currentValue + 1, currentValue) != currentValue); - - WakeArbiterThreads(address, count); - - _context.CriticalSection.Leave(); - - return Result.Success; - } - - public Result SignalAndModifyIfEqual(ulong address, int value, int count) - { - _context.CriticalSection.Enter(); - - int addend; - - // The value is decremented if the number of threads waiting is less - // or equal to the Count of threads to be signaled, or Count is zero - // or negative. It is incremented if there are no threads waiting. - int waitingCount = 0; - - foreach (KThread thread in _arbiterThreads.Where(x => x.MutexAddress == address)) - { - if (++waitingCount >= count) - { - break; - } - } - - if (waitingCount > 0) - { - if (count <= 0) - { - addend = -2; - } - else if (waitingCount < count) - { - addend = -1; - } - else - { - addend = 0; - } - } - else - { - addend = 1; - } - - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - - if (!currentProcess.CpuMemory.IsMapped(address)) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - ref int valueRef = ref currentProcess.CpuMemory.GetRef<int>(address); - - int currentValue; - - do - { - currentValue = valueRef; - - if (currentValue != value) - { - _context.CriticalSection.Leave(); - - return KernelResult.InvalidState; - } - } - while (Interlocked.CompareExchange(ref valueRef, currentValue + addend, currentValue) != currentValue); - - WakeArbiterThreads(address, count); - - _context.CriticalSection.Leave(); - - return Result.Success; - } - - private void WakeArbiterThreads(ulong address, int count) - { - static void RemoveArbiterThread(KThread thread) - { - thread.SignaledObj = null; - thread.ObjSyncResult = Result.Success; - - thread.ReleaseAndResume(); - - thread.WaitingInArbitration = false; - } - - WakeThreads(_arbiterThreads, count, RemoveArbiterThread, x => x.MutexAddress == address); - } - - private static void WakeThreads( - List<KThread> threads, - int count, - Action<KThread> removeCallback, - Func<KThread, bool> predicate) - { - var candidates = threads.Where(predicate).OrderBy(x => x.DynamicPriority); - var toSignal = (count > 0 ? candidates.Take(count) : candidates).ToArray(); - - foreach (KThread thread in toSignal) - { - removeCallback(thread); - threads.Remove(thread); - } - } - } -} diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs deleted file mode 100644 index 891e632f..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KConditionVariable.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Collections.Generic; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - static class KConditionVariable - { - public static void Wait(KernelContext context, LinkedList<KThread> threadList, object mutex, long timeout) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - context.CriticalSection.Enter(); - - Monitor.Exit(mutex); - - currentThread.Withholder = threadList; - - currentThread.Reschedule(ThreadSchedState.Paused); - - currentThread.WithholderNode = threadList.AddLast(currentThread); - - if (currentThread.TerminationRequested) - { - threadList.Remove(currentThread.WithholderNode); - - currentThread.Reschedule(ThreadSchedState.Running); - - currentThread.Withholder = null; - - context.CriticalSection.Leave(); - } - else - { - if (timeout > 0) - { - context.TimeManager.ScheduleFutureInvocation(currentThread, timeout); - } - - context.CriticalSection.Leave(); - - if (timeout > 0) - { - context.TimeManager.UnscheduleFutureInvocation(currentThread); - } - } - - Monitor.Enter(mutex); - } - - public static void NotifyAll(KernelContext context, LinkedList<KThread> threadList) - { - context.CriticalSection.Enter(); - - LinkedListNode<KThread> node = threadList.First; - - for (; node != null; node = threadList.First) - { - KThread thread = node.Value; - - threadList.Remove(thread.WithholderNode); - - thread.Withholder = null; - - thread.Reschedule(ThreadSchedState.Running); - } - - context.CriticalSection.Leave(); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs deleted file mode 100644 index 1d61f2f0..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KCriticalSection - { - private readonly KernelContext _context; - private readonly object _lock; - private int _recursionCount; - - public object Lock => _lock; - - public KCriticalSection(KernelContext context) - { - _context = context; - _lock = new object(); - } - - public void Enter() - { - Monitor.Enter(_lock); - - _recursionCount++; - } - - public void Leave() - { - if (_recursionCount == 0) - { - return; - } - - if (--_recursionCount == 0) - { - ulong scheduledCoresMask = KScheduler.SelectThreads(_context); - - Monitor.Exit(_lock); - - KThread currentThread = KernelStatic.GetCurrentThread(); - bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable; - if (isCurrentThreadSchedulable) - { - KScheduler.EnableScheduling(_context, scheduledCoresMask); - } - else - { - KScheduler.EnableSchedulingFromForeignThread(_context, scheduledCoresMask); - - // If the thread exists but is not schedulable, we still want to suspend - // it if it's not runnable. That allows the kernel to still block HLE threads - // even if they are not scheduled on guest cores. - if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running) - { - currentThread.SchedulerWaitEvent.WaitOne(); - } - } - } - else - { - Monitor.Exit(_lock); - } - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs deleted file mode 100644 index da3e217b..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KEvent - { - public KReadableEvent ReadableEvent { get; private set; } - public KWritableEvent WritableEvent { get; private set; } - - public KEvent(KernelContext context) - { - ReadableEvent = new KReadableEvent(context, this); - WritableEvent = new KWritableEvent(context, this); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs deleted file mode 100644 index 14fba704..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs +++ /dev/null @@ -1,286 +0,0 @@ -using System.Collections.Generic; -using System.Numerics; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KPriorityQueue - { - private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore; - private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore; - - private readonly long[] _scheduledPrioritiesPerCore; - private readonly long[] _suggestedPrioritiesPerCore; - - public KPriorityQueue() - { - _suggestedThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][]; - _scheduledThreadsPerPrioPerCore = new LinkedList<KThread>[KScheduler.PrioritiesCount][]; - - for (int prio = 0; prio < KScheduler.PrioritiesCount; prio++) - { - _suggestedThreadsPerPrioPerCore[prio] = new LinkedList<KThread>[KScheduler.CpuCoresCount]; - _scheduledThreadsPerPrioPerCore[prio] = new LinkedList<KThread>[KScheduler.CpuCoresCount]; - - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - _suggestedThreadsPerPrioPerCore[prio][core] = new LinkedList<KThread>(); - _scheduledThreadsPerPrioPerCore[prio][core] = new LinkedList<KThread>(); - } - } - - _scheduledPrioritiesPerCore = new long[KScheduler.CpuCoresCount]; - _suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount]; - } - - public readonly ref struct KThreadEnumerable - { - readonly LinkedList<KThread>[][] _listPerPrioPerCore; - readonly long[] _prios; - readonly int _core; - - public KThreadEnumerable(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core) - { - _listPerPrioPerCore = listPerPrioPerCore; - _prios = prios; - _core = core; - } - - public Enumerator GetEnumerator() - { - return new Enumerator(_listPerPrioPerCore, _prios, _core); - } - - public ref struct Enumerator - { - private readonly LinkedList<KThread>[][] _listPerPrioPerCore; - private readonly int _core; - private long _prioMask; - private int _prio; - private LinkedList<KThread> _list; - private LinkedListNode<KThread> _node; - - public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core) - { - _listPerPrioPerCore = listPerPrioPerCore; - _core = core; - _prioMask = prios[core]; - _prio = BitOperations.TrailingZeroCount(_prioMask); - _prioMask &= ~(1L << _prio); - } - - public KThread Current => _node?.Value; - - public bool MoveNext() - { - _node = _node?.Next; - - if (_node == null) - { - if (!MoveNextListAndFirstNode()) - { - return false; - } - } - - return _node != null; - } - - private bool MoveNextListAndFirstNode() - { - if (_prio < KScheduler.PrioritiesCount) - { - _list = _listPerPrioPerCore[_prio][_core]; - - _node = _list.First; - - _prio = BitOperations.TrailingZeroCount(_prioMask); - - _prioMask &= ~(1L << _prio); - - return true; - } - else - { - _list = null; - _node = null; - return false; - } - } - } - } - - public KThreadEnumerable ScheduledThreads(int core) - { - return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core); - } - - public KThreadEnumerable SuggestedThreads(int core) - { - return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core); - } - - public KThread ScheduledThreadsFirstOrDefault(int core) - { - return ScheduledThreadsElementAtOrDefault(core, 0); - } - - public KThread ScheduledThreadsElementAtOrDefault(int core, int index) - { - int currentIndex = 0; - foreach (var scheduledThread in ScheduledThreads(core)) - { - if (currentIndex == index) - { - return scheduledThread; - } - else - { - currentIndex++; - } - } - - return null; - } - - public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority) - { - foreach (var scheduledThread in ScheduledThreads(core)) - { - if (scheduledThread.DynamicPriority == dynamicPriority) - { - return scheduledThread; - } - } - - return null; - } - - public bool HasScheduledThreads(int core) - { - return ScheduledThreadsFirstOrDefault(core) != null; - } - - public void TransferToCore(int prio, int dstCore, KThread thread) - { - int srcCore = thread.ActiveCore; - if (srcCore == dstCore) - { - return; - } - - thread.ActiveCore = dstCore; - - if (srcCore >= 0) - { - Unschedule(prio, srcCore, thread); - } - - if (dstCore >= 0) - { - Unsuggest(prio, dstCore, thread); - Schedule(prio, dstCore, thread); - } - - if (srcCore >= 0) - { - Suggest(prio, srcCore, thread); - } - } - - public void Suggest(int prio, int core, KThread thread) - { - if (prio >= KScheduler.PrioritiesCount) - { - return; - } - - thread.SiblingsPerCore[core] = SuggestedQueue(prio, core).AddFirst(thread); - - _suggestedPrioritiesPerCore[core] |= 1L << prio; - } - - public void Unsuggest(int prio, int core, KThread thread) - { - if (prio >= KScheduler.PrioritiesCount) - { - return; - } - - LinkedList<KThread> queue = SuggestedQueue(prio, core); - - queue.Remove(thread.SiblingsPerCore[core]); - - if (queue.First == null) - { - _suggestedPrioritiesPerCore[core] &= ~(1L << prio); - } - } - - public void Schedule(int prio, int core, KThread thread) - { - if (prio >= KScheduler.PrioritiesCount) - { - return; - } - - thread.SiblingsPerCore[core] = ScheduledQueue(prio, core).AddLast(thread); - - _scheduledPrioritiesPerCore[core] |= 1L << prio; - } - - public void SchedulePrepend(int prio, int core, KThread thread) - { - if (prio >= KScheduler.PrioritiesCount) - { - return; - } - - thread.SiblingsPerCore[core] = ScheduledQueue(prio, core).AddFirst(thread); - - _scheduledPrioritiesPerCore[core] |= 1L << prio; - } - - public KThread Reschedule(int prio, int core, KThread thread) - { - if (prio >= KScheduler.PrioritiesCount) - { - return null; - } - - LinkedList<KThread> queue = ScheduledQueue(prio, core); - - queue.Remove(thread.SiblingsPerCore[core]); - - thread.SiblingsPerCore[core] = queue.AddLast(thread); - - return queue.First.Value; - } - - public void Unschedule(int prio, int core, KThread thread) - { - if (prio >= KScheduler.PrioritiesCount) - { - return; - } - - LinkedList<KThread> queue = ScheduledQueue(prio, core); - - queue.Remove(thread.SiblingsPerCore[core]); - - if (queue.First == null) - { - _scheduledPrioritiesPerCore[core] &= ~(1L << prio); - } - } - - private LinkedList<KThread> SuggestedQueue(int prio, int core) - { - return _suggestedThreadsPerPrioPerCore[prio][core]; - } - - private LinkedList<KThread> ScheduledQueue(int prio, int core) - { - return _scheduledThreadsPerPrioPerCore[prio][core]; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs deleted file mode 100644 index d9e7befa..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KReadableEvent.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.Horizon.Common; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KReadableEvent : KSynchronizationObject - { - private readonly KEvent _parent; - - private bool _signaled; - - public KReadableEvent(KernelContext context, KEvent parent) : base(context) - { - _parent = parent; - } - - public override void Signal() - { - KernelContext.CriticalSection.Enter(); - - if (!_signaled) - { - _signaled = true; - - base.Signal(); - } - - KernelContext.CriticalSection.Leave(); - } - - public Result Clear() - { - _signaled = false; - - return Result.Success; - } - - public Result ClearIfSignaled() - { - Result result; - - KernelContext.CriticalSection.Enter(); - - if (_signaled) - { - _signaled = false; - - result = Result.Success; - } - else - { - result = KernelResult.InvalidState; - } - - KernelContext.CriticalSection.Leave(); - - return result; - } - - public override bool IsSignaled() - { - return _signaled; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs deleted file mode 100644 index b9de7d9c..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ /dev/null @@ -1,661 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.HLE.HOS.Kernel.Process; -using System; -using System.Numerics; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - partial class KScheduler : IDisposable - { - public const int PrioritiesCount = 64; - public const int CpuCoresCount = 4; - - private const int RoundRobinTimeQuantumMs = 10; - - private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 }; - - private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount]; - - private readonly KernelContext _context; - private readonly int _coreId; - - private struct SchedulingState - { - public volatile bool NeedsScheduling; - public volatile KThread SelectedThread; - } - - private SchedulingState _state; - - private AutoResetEvent _idleInterruptEvent; - private readonly object _idleInterruptEventLock; - - private KThread _previousThread; - private KThread _currentThread; - private readonly KThread _idleThread; - - public KThread PreviousThread => _previousThread; - public KThread CurrentThread => _currentThread; - public long LastContextSwitchTime { get; private set; } - public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning; - - public KScheduler(KernelContext context, int coreId) - { - _context = context; - _coreId = coreId; - - _idleInterruptEvent = new AutoResetEvent(false); - _idleInterruptEventLock = new object(); - - KThread idleThread = CreateIdleThread(context, coreId); - - _currentThread = idleThread; - _idleThread = idleThread; - - idleThread.StartHostThread(); - idleThread.SchedulerWaitEvent.Set(); - } - - private KThread CreateIdleThread(KernelContext context, int cpuCore) - { - KThread idleThread = new KThread(context); - - idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop); - - return idleThread; - } - - public static ulong SelectThreads(KernelContext context) - { - if (context.ThreadReselectionRequested) - { - return SelectThreadsImpl(context); - } - else - { - return 0UL; - } - } - - private static ulong SelectThreadsImpl(KernelContext context) - { - context.ThreadReselectionRequested = false; - - ulong scheduledCoresMask = 0UL; - - for (int core = 0; core < CpuCoresCount; core++) - { - KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core); - - if (thread != null && - thread.Owner != null && - thread.Owner.PinnedThreads[core] != null && - thread.Owner.PinnedThreads[core] != thread) - { - KThread candidate = thread.Owner.PinnedThreads[core]; - - if (candidate.KernelWaitersCount == 0 && !thread.Owner.IsExceptionUserThread(candidate)) - { - if (candidate.SchedFlags == ThreadSchedState.Running) - { - thread = candidate; - } - else - { - thread = null; - } - } - } - - scheduledCoresMask |= context.Schedulers[core].SelectThread(thread); - } - - for (int core = 0; core < CpuCoresCount; core++) - { - // If the core is not idle (there's already a thread running on it), - // then we don't need to attempt load balancing. - if (context.PriorityQueue.HasScheduledThreads(core)) - { - continue; - } - - Array.Fill(_srcCoresHighestPrioThreads, 0); - - int srcCoresHighestPrioThreadsCount = 0; - - KThread dst = null; - - // Select candidate threads that could run on this core. - // Give preference to threads that are not yet selected. - foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) - { - if (suggested.ActiveCore < 0 || suggested != context.Schedulers[suggested.ActiveCore]._state.SelectedThread) - { - dst = suggested; - break; - } - - _srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore; - } - - // Not yet selected candidate found. - if (dst != null) - { - // Priorities < 2 are used for the kernel message dispatching - // threads, we should skip load balancing entirely. - if (dst.DynamicPriority >= 2) - { - context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst); - - scheduledCoresMask |= context.Schedulers[core].SelectThread(dst); - } - - continue; - } - - // All candidates are already selected, choose the best one - // (the first one that doesn't make the source core idle if moved). - for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++) - { - int srcCore = _srcCoresHighestPrioThreads[index]; - - KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1); - - if (src != null) - { - // Run the second thread on the queue on the source core, - // move the first one to the current core. - KThread origSelectedCoreSrc = context.Schedulers[srcCore]._state.SelectedThread; - - scheduledCoresMask |= context.Schedulers[srcCore].SelectThread(src); - - context.PriorityQueue.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc); - - scheduledCoresMask |= context.Schedulers[core].SelectThread(origSelectedCoreSrc); - } - } - } - - return scheduledCoresMask; - } - - private ulong SelectThread(KThread nextThread) - { - KThread previousThread = _state.SelectedThread; - - if (previousThread != nextThread) - { - if (previousThread != null) - { - previousThread.LastScheduledTime = PerformanceCounter.ElapsedTicks; - } - - _state.SelectedThread = nextThread; - _state.NeedsScheduling = true; - return 1UL << _coreId; - } - else - { - return 0UL; - } - } - - public static void EnableScheduling(KernelContext context, ulong scheduledCoresMask) - { - KScheduler currentScheduler = context.Schedulers[KernelStatic.GetCurrentThread().CurrentCore]; - - // Note that "RescheduleCurrentCore" will block, so "RescheduleOtherCores" must be done first. - currentScheduler.RescheduleOtherCores(scheduledCoresMask); - currentScheduler.RescheduleCurrentCore(); - } - - public static void EnableSchedulingFromForeignThread(KernelContext context, ulong scheduledCoresMask) - { - RescheduleOtherCores(context, scheduledCoresMask); - } - - private void RescheduleCurrentCore() - { - if (_state.NeedsScheduling) - { - Schedule(); - } - } - - private void RescheduleOtherCores(ulong scheduledCoresMask) - { - RescheduleOtherCores(_context, scheduledCoresMask & ~(1UL << _coreId)); - } - - private static void RescheduleOtherCores(KernelContext context, ulong scheduledCoresMask) - { - while (scheduledCoresMask != 0) - { - int coreToSignal = BitOperations.TrailingZeroCount(scheduledCoresMask); - - KThread threadToSignal = context.Schedulers[coreToSignal]._currentThread; - - // Request the thread running on that core to stop and reschedule, if we have one. - if (threadToSignal != context.Schedulers[coreToSignal]._idleThread) - { - threadToSignal.Context.RequestInterrupt(); - } - - // If the core is idle, ensure that the idle thread is awaken. - context.Schedulers[coreToSignal]._idleInterruptEvent.Set(); - - scheduledCoresMask &= ~(1UL << coreToSignal); - } - } - - private void IdleThreadLoop() - { - while (_context.Running) - { - _state.NeedsScheduling = false; - Thread.MemoryBarrier(); - KThread nextThread = PickNextThread(_state.SelectedThread); - - if (_idleThread != nextThread) - { - _idleThread.SchedulerWaitEvent.Reset(); - WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, _idleThread.SchedulerWaitEvent); - } - - _idleInterruptEvent.WaitOne(); - } - - lock (_idleInterruptEventLock) - { - _idleInterruptEvent.Dispose(); - _idleInterruptEvent = null; - } - } - - public void Schedule() - { - _state.NeedsScheduling = false; - Thread.MemoryBarrier(); - KThread currentThread = KernelStatic.GetCurrentThread(); - KThread selectedThread = _state.SelectedThread; - - // If the thread is already scheduled and running on the core, we have nothing to do. - if (currentThread == selectedThread) - { - return; - } - - currentThread.SchedulerWaitEvent.Reset(); - currentThread.ThreadContext.Unlock(); - - // Wake all the threads that might be waiting until this thread context is unlocked. - for (int core = 0; core < CpuCoresCount; core++) - { - _context.Schedulers[core]._idleInterruptEvent.Set(); - } - - KThread nextThread = PickNextThread(selectedThread); - - if (currentThread.Context.Running) - { - // Wait until this thread is scheduled again, and allow the next thread to run. - WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent); - } - else - { - // Allow the next thread to run. - nextThread.SchedulerWaitEvent.Set(); - - // We don't need to wait since the thread is exiting, however we need to - // make sure this thread will never call the scheduler again, since it is - // no longer assigned to a core. - currentThread.MakeUnschedulable(); - - // Just to be sure, set the core to a invalid value. - // This will trigger a exception if it attempts to call schedule again, - // rather than leaving the scheduler in a invalid state. - currentThread.CurrentCore = -1; - } - } - - private KThread PickNextThread(KThread selectedThread) - { - while (true) - { - if (selectedThread != null) - { - // Try to run the selected thread. - // We need to acquire the context lock to be sure the thread is not - // already running on another core. If it is, then we return here - // and the caller should try again once there is something available for scheduling. - // The thread currently running on the core should have been requested to - // interrupt so this is not expected to take long. - // The idle thread must also be paused if we are scheduling a thread - // on the core, as the scheduled thread will handle the next switch. - if (selectedThread.ThreadContext.Lock()) - { - SwitchTo(selectedThread); - - if (!_state.NeedsScheduling) - { - return selectedThread; - } - - selectedThread.ThreadContext.Unlock(); - } - else - { - return _idleThread; - } - } - else - { - // The core is idle now, make sure that the idle thread can run - // and switch the core when a thread is available. - SwitchTo(null); - return _idleThread; - } - - _state.NeedsScheduling = false; - Thread.MemoryBarrier(); - selectedThread = _state.SelectedThread; - } - } - - private void SwitchTo(KThread nextThread) - { - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - KThread currentThread = KernelStatic.GetCurrentThread(); - - nextThread ??= _idleThread; - - if (currentThread != nextThread) - { - long previousTicks = LastContextSwitchTime; - long currentTicks = PerformanceCounter.ElapsedTicks; - long ticksDelta = currentTicks - previousTicks; - - currentThread.AddCpuTime(ticksDelta); - - if (currentProcess != null) - { - currentProcess.AddCpuTime(ticksDelta); - } - - LastContextSwitchTime = currentTicks; - - if (currentProcess != null) - { - _previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null; - } - else if (currentThread == _idleThread) - { - _previousThread = null; - } - } - - if (nextThread.CurrentCore != _coreId) - { - nextThread.CurrentCore = _coreId; - } - - _currentThread = nextThread; - } - - public static void PreemptionThreadLoop(KernelContext context) - { - while (context.Running) - { - context.CriticalSection.Enter(); - - for (int core = 0; core < CpuCoresCount; core++) - { - RotateScheduledQueue(context, core, PreemptionPriorities[core]); - } - - context.CriticalSection.Leave(); - - Thread.Sleep(RoundRobinTimeQuantumMs); - } - } - - private static void RotateScheduledQueue(KernelContext context, int core, int prio) - { - KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio); - KThread nextThread = null; - - // Yield priority queue. - if (selectedThread != null) - { - nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread); - } - - static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate) - { - foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) - { - int suggestedCore = suggested.ActiveCore; - if (suggestedCore >= 0) - { - KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore); - - if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2)) - { - continue; - } - } - - // If the candidate was scheduled after the current thread, then it's not worth it. - if (nextThread == selectedThread || - nextThread == null || - nextThread.LastScheduledTime >= suggested.LastScheduledTime) - { - if (predicate(suggested)) - { - return suggested; - } - } - } - - return null; - } - - // Select candidate threads that could run on this core. - // Only take into account threads that are not yet selected. - KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio); - - if (dst != null) - { - context.PriorityQueue.TransferToCore(prio, core, dst); - } - - // If the priority of the currently selected thread is lower or same as the preemption priority, - // then try to migrate a thread with lower priority. - KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core); - - if (bestCandidate != null && bestCandidate.DynamicPriority >= prio) - { - dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority); - - if (dst != null) - { - context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst); - } - } - - context.ThreadReselectionRequested = true; - } - - public static void Yield(KernelContext context) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - if (!currentThread.IsSchedulable) - { - return; - } - - context.CriticalSection.Enter(); - - if (currentThread.SchedFlags != ThreadSchedState.Running) - { - context.CriticalSection.Leave(); - return; - } - - KThread nextThread = context.PriorityQueue.Reschedule(currentThread.DynamicPriority, currentThread.ActiveCore, currentThread); - - if (nextThread != currentThread) - { - context.ThreadReselectionRequested = true; - } - - context.CriticalSection.Leave(); - } - - public static void YieldWithLoadBalancing(KernelContext context) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - if (!currentThread.IsSchedulable) - { - return; - } - - context.CriticalSection.Enter(); - - if (currentThread.SchedFlags != ThreadSchedState.Running) - { - context.CriticalSection.Leave(); - return; - } - - int prio = currentThread.DynamicPriority; - int core = currentThread.ActiveCore; - - // Move current thread to the end of the queue. - KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread); - - static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority) - { - foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) - { - int suggestedCore = suggested.ActiveCore; - if (suggestedCore >= 0) - { - KThread selectedSuggestedCore = context.Schedulers[suggestedCore]._state.SelectedThread; - - if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2)) - { - continue; - } - } - - // If the candidate was scheduled after the current thread, then it's not worth it, - // unless the priority is higher than the current one. - if (suggested.LastScheduledTime <= nextThread.LastScheduledTime || - suggested.DynamicPriority < nextThread.DynamicPriority) - { - if (suggested.DynamicPriority <= lessThanOrEqualPriority) - { - return suggested; - } - } - } - - return null; - } - - KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio); - - if (dst != null) - { - context.PriorityQueue.TransferToCore(dst.DynamicPriority, core, dst); - - context.ThreadReselectionRequested = true; - } - else if (currentThread != nextThread) - { - context.ThreadReselectionRequested = true; - } - - context.CriticalSection.Leave(); - } - - public static void YieldToAnyThread(KernelContext context) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - if (!currentThread.IsSchedulable) - { - return; - } - - context.CriticalSection.Enter(); - - if (currentThread.SchedFlags != ThreadSchedState.Running) - { - context.CriticalSection.Leave(); - return; - } - - int core = currentThread.ActiveCore; - - context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread); - - if (!context.PriorityQueue.HasScheduledThreads(core)) - { - KThread selectedThread = null; - - foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core)) - { - int suggestedCore = suggested.ActiveCore; - - if (suggestedCore < 0) - { - continue; - } - - KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore); - - if (firstCandidate == suggested) - { - continue; - } - - if (firstCandidate == null || firstCandidate.DynamicPriority >= 2) - { - context.PriorityQueue.TransferToCore(suggested.DynamicPriority, core, suggested); - } - - selectedThread = suggested; - break; - } - - if (currentThread != selectedThread) - { - context.ThreadReselectionRequested = true; - } - } - else - { - context.ThreadReselectionRequested = true; - } - - context.CriticalSection.Leave(); - } - - public void Dispose() - { - // Ensure that the idle thread is not blocked and can exit. - lock (_idleInterruptEventLock) - { - if (_idleInterruptEvent != null) - { - _idleInterruptEvent.Set(); - } - } - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs deleted file mode 100644 index 9c196810..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs +++ /dev/null @@ -1,142 +0,0 @@ -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 diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs deleted file mode 100644 index 63396468..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ /dev/null @@ -1,1438 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.HLE.HOS.Kernel.SupervisorCall; -using Ryujinx.Horizon.Common; -using System; -using System.Collections.Generic; -using System.Numerics; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KThread : KSynchronizationObject, IKFutureSchedulerObject - { - private const int TlsUserDisableCountOffset = 0x100; - private const int TlsUserInterruptFlagOffset = 0x102; - - public const int MaxWaitSyncObjects = 64; - - private ManualResetEvent _schedulerWaitEvent; - - public ManualResetEvent SchedulerWaitEvent => _schedulerWaitEvent; - - public Thread HostThread { get; private set; } - - public IExecutionContext Context { get; private set; } - - public KThreadContext ThreadContext { get; private set; } - - public int DynamicPriority { get; set; } - public ulong AffinityMask { get; set; } - - public ulong ThreadUid { get; private set; } - - private long _totalTimeRunning; - - public long TotalTimeRunning => _totalTimeRunning; - - public KSynchronizationObject SignaledObj { get; set; } - - public ulong CondVarAddress { get; set; } - - private ulong _entrypoint; - private ThreadStart _customThreadStart; - private bool _forcedUnschedulable; - - public bool IsSchedulable => _customThreadStart == null && !_forcedUnschedulable; - - public ulong MutexAddress { get; set; } - public int KernelWaitersCount { get; private set; } - - public KProcess Owner { get; private set; } - - private ulong _tlsAddress; - - public ulong TlsAddress => _tlsAddress; - - public KSynchronizationObject[] WaitSyncObjects { get; } - public int[] WaitSyncHandles { get; } - - public long LastScheduledTime { get; set; } - - public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; } - - public LinkedList<KThread> Withholder { get; set; } - public LinkedListNode<KThread> WithholderNode { get; set; } - - public LinkedListNode<KThread> ProcessListNode { get; set; } - - private LinkedList<KThread> _mutexWaiters; - private LinkedListNode<KThread> _mutexWaiterNode; - - private LinkedList<KThread> _pinnedWaiters; - - public KThread MutexOwner { get; private set; } - - public int ThreadHandleForUserMutex { get; set; } - - private ThreadSchedState _forcePauseFlags; - private ThreadSchedState _forcePausePermissionFlags; - - public Result ObjSyncResult { get; set; } - - public int BasePriority { get; set; } - public int PreferredCore { get; set; } - - public int CurrentCore { get; set; } - public int ActiveCore { get; set; } - - public bool IsPinned { get; private set; } - - private ulong _originalAffinityMask; - private int _originalPreferredCore; - private int _originalBasePriority; - private int _coreMigrationDisableCount; - - public ThreadSchedState SchedFlags { get; private set; } - - private int _shallBeTerminated; - - private bool ShallBeTerminated => _shallBeTerminated != 0; - - public bool TerminationRequested => ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending; - - public bool SyncCancelled { get; set; } - public bool WaitingSync { get; set; } - - private int _hasExited; - private bool _hasBeenInitialized; - private bool _hasBeenReleased; - - public bool WaitingInArbitration { get; set; } - - private object _activityOperationLock; - - public KThread(KernelContext context) : base(context) - { - WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects]; - WaitSyncHandles = new int[MaxWaitSyncObjects]; - - SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount]; - - _mutexWaiters = new LinkedList<KThread>(); - _pinnedWaiters = new LinkedList<KThread>(); - - _activityOperationLock = new object(); - } - - public Result Initialize( - ulong entrypoint, - ulong argsPtr, - ulong stackTop, - int priority, - int cpuCore, - KProcess owner, - ThreadType type, - ThreadStart customThreadStart = null) - { - if ((uint)type > 3) - { - throw new ArgumentException($"Invalid thread type \"{type}\"."); - } - - PreferredCore = cpuCore; - AffinityMask |= 1UL << cpuCore; - - SchedFlags = type == ThreadType.Dummy - ? ThreadSchedState.Running - : ThreadSchedState.None; - - ActiveCore = cpuCore; - ObjSyncResult = KernelResult.ThreadNotStarted; - DynamicPriority = priority; - BasePriority = priority; - CurrentCore = cpuCore; - IsPinned = false; - - _entrypoint = entrypoint; - _customThreadStart = customThreadStart; - - if (type == ThreadType.User) - { - if (owner.AllocateThreadLocalStorage(out _tlsAddress) != Result.Success) - { - return KernelResult.OutOfMemory; - } - - MemoryHelper.FillWithZeros(owner.CpuMemory, _tlsAddress, KTlsPageInfo.TlsEntrySize); - } - - bool is64Bits; - - if (owner != null) - { - Owner = owner; - - owner.IncrementReferenceCount(); - owner.IncrementThreadCount(); - - is64Bits = owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit); - } - else - { - is64Bits = true; - } - - HostThread = new Thread(ThreadStart); - - Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext(); - - ThreadContext = new KThreadContext(Context); - - Context.IsAarch32 = !is64Bits; - - Context.SetX(0, argsPtr); - - if (is64Bits) - { - Context.SetX(18, KSystemControl.GenerateRandom() | 1); - Context.SetX(31, stackTop); - } - else - { - Context.SetX(13, (uint)stackTop); - } - - Context.TpidrroEl0 = (long)_tlsAddress; - - ThreadUid = KernelContext.NewThreadUid(); - - HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}"; - - _hasBeenInitialized = true; - - _forcePausePermissionFlags = ThreadSchedState.ForcePauseMask; - - if (owner != null) - { - owner.AddThread(this); - - if (owner.IsPaused) - { - KernelContext.CriticalSection.Enter(); - - if (TerminationRequested) - { - KernelContext.CriticalSection.Leave(); - - return Result.Success; - } - - _forcePauseFlags |= ThreadSchedState.ProcessPauseFlag; - - CombineForcePauseFlags(); - - KernelContext.CriticalSection.Leave(); - } - } - - return Result.Success; - } - - public Result Start() - { - if (!KernelContext.KernelInitialized) - { - KernelContext.CriticalSection.Enter(); - - if (!TerminationRequested) - { - _forcePauseFlags |= ThreadSchedState.KernelInitPauseFlag; - - CombineForcePauseFlags(); - } - - KernelContext.CriticalSection.Leave(); - } - - Result result = KernelResult.ThreadTerminating; - - KernelContext.CriticalSection.Enter(); - - if (!ShallBeTerminated) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - while (SchedFlags != ThreadSchedState.TerminationPending && (currentThread == null || !currentThread.TerminationRequested)) - { - if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None) - { - result = KernelResult.InvalidState; - break; - } - - if (currentThread == null || currentThread._forcePauseFlags == ThreadSchedState.None) - { - if (Owner != null && _forcePauseFlags != ThreadSchedState.None) - { - CombineForcePauseFlags(); - } - - SetNewSchedFlags(ThreadSchedState.Running); - - StartHostThread(); - - result = Result.Success; - break; - } - else - { - currentThread.CombineForcePauseFlags(); - - KernelContext.CriticalSection.Leave(); - KernelContext.CriticalSection.Enter(); - - if (currentThread.ShallBeTerminated) - { - break; - } - } - } - } - - KernelContext.CriticalSection.Leave(); - - return result; - } - - public ThreadSchedState PrepareForTermination() - { - KernelContext.CriticalSection.Enter(); - - if (Owner != null && Owner.PinnedThreads[KernelStatic.GetCurrentThread().CurrentCore] == this) - { - Owner.UnpinThread(this); - } - - ThreadSchedState result; - - if (Interlocked.Exchange(ref _shallBeTerminated, 1) == 0) - { - if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.None) - { - SchedFlags = ThreadSchedState.TerminationPending; - } - else - { - if (_forcePauseFlags != ThreadSchedState.None) - { - _forcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag; - - ThreadSchedState oldSchedFlags = SchedFlags; - - SchedFlags &= ThreadSchedState.LowMask; - - AdjustScheduling(oldSchedFlags); - } - - if (BasePriority >= 0x10) - { - SetPriority(0xF); - } - - if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Running) - { - // TODO: GIC distributor stuffs (sgir changes ect) - Context.RequestInterrupt(); - } - - SignaledObj = null; - ObjSyncResult = KernelResult.ThreadTerminating; - - ReleaseAndResume(); - } - } - - result = SchedFlags; - - KernelContext.CriticalSection.Leave(); - - return result & ThreadSchedState.LowMask; - } - - public void Terminate() - { - ThreadSchedState state = PrepareForTermination(); - - if (state != ThreadSchedState.TerminationPending) - { - KernelContext.Synchronization.WaitFor(new KSynchronizationObject[] { this }, -1, out _); - } - } - - public void HandlePostSyscall() - { - ThreadSchedState state; - - do - { - if (TerminationRequested) - { - Exit(); - - // As the death of the thread is handled by the CPU emulator, we differ from the official kernel and return here. - break; - } - - KernelContext.CriticalSection.Enter(); - - if (TerminationRequested) - { - state = ThreadSchedState.TerminationPending; - } - else - { - if (_forcePauseFlags != ThreadSchedState.None) - { - CombineForcePauseFlags(); - } - - state = ThreadSchedState.Running; - } - - KernelContext.CriticalSection.Leave(); - } while (state == ThreadSchedState.TerminationPending); - } - - public void Exit() - { - // TODO: Debug event. - - if (Owner != null) - { - Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1); - - _hasBeenReleased = true; - } - - KernelContext.CriticalSection.Enter(); - - _forcePauseFlags &= ~ThreadSchedState.ForcePauseMask; - _forcePausePermissionFlags = 0; - - bool decRef = ExitImpl(); - - Context.StopRunning(); - - KernelContext.CriticalSection.Leave(); - - if (decRef) - { - DecrementReferenceCount(); - } - } - - private bool ExitImpl() - { - KernelContext.CriticalSection.Enter(); - - SetNewSchedFlags(ThreadSchedState.TerminationPending); - - bool decRef = Interlocked.Exchange(ref _hasExited, 1) == 0; - - Signal(); - - KernelContext.CriticalSection.Leave(); - - return decRef; - } - - private int GetEffectiveRunningCore() - { - for (int coreNumber = 0; coreNumber < KScheduler.CpuCoresCount; coreNumber++) - { - if (KernelContext.Schedulers[coreNumber].CurrentThread == this) - { - return coreNumber; - } - } - - return -1; - } - - public Result Sleep(long timeout) - { - KernelContext.CriticalSection.Enter(); - - if (TerminationRequested) - { - KernelContext.CriticalSection.Leave(); - - return KernelResult.ThreadTerminating; - } - - SetNewSchedFlags(ThreadSchedState.Paused); - - if (timeout > 0) - { - KernelContext.TimeManager.ScheduleFutureInvocation(this, timeout); - } - - KernelContext.CriticalSection.Leave(); - - if (timeout > 0) - { - KernelContext.TimeManager.UnscheduleFutureInvocation(this); - } - - return Result.Success; - } - - public void SetPriority(int priority) - { - KernelContext.CriticalSection.Enter(); - - if (IsPinned) - { - _originalBasePriority = priority; - } - else - { - BasePriority = priority; - } - - UpdatePriorityInheritance(); - - KernelContext.CriticalSection.Leave(); - } - - public void Suspend(ThreadSchedState type) - { - _forcePauseFlags |= type; - - CombineForcePauseFlags(); - } - - public void Resume(ThreadSchedState type) - { - ThreadSchedState oldForcePauseFlags = _forcePauseFlags; - - _forcePauseFlags &= ~type; - - if ((oldForcePauseFlags & ~type) == ThreadSchedState.None) - { - ThreadSchedState oldSchedFlags = SchedFlags; - - SchedFlags &= ThreadSchedState.LowMask; - - AdjustScheduling(oldSchedFlags); - } - } - - public Result SetActivity(bool pause) - { - lock (_activityOperationLock) - { - Result result = Result.Success; - - KernelContext.CriticalSection.Enter(); - - ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask; - - if (lowNibble != ThreadSchedState.Paused && lowNibble != ThreadSchedState.Running) - { - KernelContext.CriticalSection.Leave(); - - return KernelResult.InvalidState; - } - - if (!TerminationRequested) - { - if (pause) - { - // Pause, the force pause flag should be clear (thread is NOT paused). - if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0) - { - Suspend(ThreadSchedState.ThreadPauseFlag); - } - else - { - result = KernelResult.InvalidState; - } - } - else - { - // Unpause, the force pause flag should be set (thread is paused). - if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0) - { - Resume(ThreadSchedState.ThreadPauseFlag); - } - else - { - result = KernelResult.InvalidState; - } - } - } - - KernelContext.CriticalSection.Leave(); - - if (result == Result.Success && pause) - { - bool isThreadRunning = true; - - while (isThreadRunning) - { - KernelContext.CriticalSection.Enter(); - - if (TerminationRequested) - { - KernelContext.CriticalSection.Leave(); - - break; - } - - isThreadRunning = false; - - if (IsPinned) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - if (currentThread.TerminationRequested) - { - KernelContext.CriticalSection.Leave(); - - result = KernelResult.ThreadTerminating; - - break; - } - - _pinnedWaiters.AddLast(currentThread); - - currentThread.Reschedule(ThreadSchedState.Paused); - } - else - { - isThreadRunning = GetEffectiveRunningCore() >= 0; - } - - KernelContext.CriticalSection.Leave(); - } - } - - return result; - } - } - - public Result GetThreadContext3(out ThreadContext context) - { - context = default; - - lock (_activityOperationLock) - { - KernelContext.CriticalSection.Enter(); - - if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0) - { - KernelContext.CriticalSection.Leave(); - - return KernelResult.InvalidState; - } - - if (!TerminationRequested) - { - context = GetCurrentContext(); - } - - KernelContext.CriticalSection.Leave(); - } - - return Result.Success; - } - - private static uint GetPsr(IExecutionContext context) - { - return context.Pstate & 0xFF0FFE20; - } - - private ThreadContext GetCurrentContext() - { - const int MaxRegistersAArch32 = 15; - const int MaxFpuRegistersAArch32 = 16; - - ThreadContext context = new ThreadContext(); - - if (Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit)) - { - for (int i = 0; i < context.Registers.Length; i++) - { - context.Registers[i] = Context.GetX(i); - } - - for (int i = 0; i < context.FpuRegisters.Length; i++) - { - context.FpuRegisters[i] = Context.GetV(i); - } - - context.Fp = Context.GetX(29); - context.Lr = Context.GetX(30); - context.Sp = Context.GetX(31); - context.Pc = Context.Pc; - context.Pstate = GetPsr(Context); - context.Tpidr = (ulong)Context.TpidrroEl0; - } - else - { - for (int i = 0; i < MaxRegistersAArch32; i++) - { - context.Registers[i] = (uint)Context.GetX(i); - } - - for (int i = 0; i < MaxFpuRegistersAArch32; i++) - { - context.FpuRegisters[i] = Context.GetV(i); - } - - context.Pc = (uint)Context.Pc; - context.Pstate = GetPsr(Context); - context.Tpidr = (uint)Context.TpidrroEl0; - } - - context.Fpcr = (uint)Context.Fpcr; - context.Fpsr = (uint)Context.Fpsr; - - return context; - } - - public void CancelSynchronization() - { - KernelContext.CriticalSection.Enter(); - - if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync) - { - SyncCancelled = true; - } - else if (Withholder != null) - { - Withholder.Remove(WithholderNode); - - SetNewSchedFlags(ThreadSchedState.Running); - - Withholder = null; - - SyncCancelled = true; - } - else - { - SignaledObj = null; - ObjSyncResult = KernelResult.Cancelled; - - SetNewSchedFlags(ThreadSchedState.Running); - - SyncCancelled = false; - } - - KernelContext.CriticalSection.Leave(); - } - - public Result SetCoreAndAffinityMask(int newCore, ulong newAffinityMask) - { - lock (_activityOperationLock) - { - KernelContext.CriticalSection.Enter(); - - bool isCoreMigrationDisabled = _coreMigrationDisableCount != 0; - - // The value -3 is "do not change the preferred core". - if (newCore == -3) - { - newCore = isCoreMigrationDisabled ? _originalPreferredCore : PreferredCore; - - if ((newAffinityMask & (1UL << newCore)) == 0) - { - KernelContext.CriticalSection.Leave(); - - return KernelResult.InvalidCombination; - } - } - - if (isCoreMigrationDisabled) - { - _originalPreferredCore = newCore; - _originalAffinityMask = newAffinityMask; - } - else - { - ulong oldAffinityMask = AffinityMask; - - PreferredCore = newCore; - AffinityMask = newAffinityMask; - - if (oldAffinityMask != newAffinityMask) - { - int oldCore = ActiveCore; - - if (oldCore >= 0 && ((AffinityMask >> oldCore) & 1) == 0) - { - if (PreferredCore < 0) - { - ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount(AffinityMask); - } - else - { - ActiveCore = PreferredCore; - } - } - - AdjustSchedulingForNewAffinity(oldAffinityMask, oldCore); - } - } - - KernelContext.CriticalSection.Leave(); - - bool targetThreadPinned = true; - - while (targetThreadPinned) - { - KernelContext.CriticalSection.Enter(); - - if (TerminationRequested) - { - KernelContext.CriticalSection.Leave(); - - break; - } - - targetThreadPinned = false; - - int coreNumber = GetEffectiveRunningCore(); - bool isPinnedThreadCurrentlyRunning = coreNumber >= 0; - - if (isPinnedThreadCurrentlyRunning && ((1UL << coreNumber) & AffinityMask) == 0) - { - if (IsPinned) - { - KThread currentThread = KernelStatic.GetCurrentThread(); - - if (currentThread.TerminationRequested) - { - KernelContext.CriticalSection.Leave(); - - return KernelResult.ThreadTerminating; - } - - _pinnedWaiters.AddLast(currentThread); - - currentThread.Reschedule(ThreadSchedState.Paused); - } - else - { - targetThreadPinned = true; - } - } - - KernelContext.CriticalSection.Leave(); - } - - return Result.Success; - } - } - - private void CombineForcePauseFlags() - { - ThreadSchedState oldFlags = SchedFlags; - ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask; - - SchedFlags = lowNibble | (_forcePauseFlags & _forcePausePermissionFlags); - - AdjustScheduling(oldFlags); - } - - private void SetNewSchedFlags(ThreadSchedState newFlags) - { - KernelContext.CriticalSection.Enter(); - - ThreadSchedState oldFlags = SchedFlags; - - SchedFlags = (oldFlags & ThreadSchedState.HighMask) | newFlags; - - if ((oldFlags & ThreadSchedState.LowMask) != newFlags) - { - AdjustScheduling(oldFlags); - } - - KernelContext.CriticalSection.Leave(); - } - - public void ReleaseAndResume() - { - KernelContext.CriticalSection.Enter(); - - if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) - { - if (Withholder != null) - { - Withholder.Remove(WithholderNode); - - SetNewSchedFlags(ThreadSchedState.Running); - - Withholder = null; - } - else - { - SetNewSchedFlags(ThreadSchedState.Running); - } - } - - KernelContext.CriticalSection.Leave(); - } - - public void Reschedule(ThreadSchedState newFlags) - { - KernelContext.CriticalSection.Enter(); - - ThreadSchedState oldFlags = SchedFlags; - - SchedFlags = (oldFlags & ThreadSchedState.HighMask) | - (newFlags & ThreadSchedState.LowMask); - - AdjustScheduling(oldFlags); - - KernelContext.CriticalSection.Leave(); - } - - public void AddMutexWaiter(KThread requester) - { - AddToMutexWaitersList(requester); - - requester.MutexOwner = this; - - UpdatePriorityInheritance(); - } - - public void RemoveMutexWaiter(KThread thread) - { - if (thread._mutexWaiterNode?.List != null) - { - _mutexWaiters.Remove(thread._mutexWaiterNode); - } - - thread.MutexOwner = null; - - UpdatePriorityInheritance(); - } - - public KThread RelinquishMutex(ulong mutexAddress, out int count) - { - count = 0; - - if (_mutexWaiters.First == null) - { - return null; - } - - KThread newMutexOwner = null; - - LinkedListNode<KThread> currentNode = _mutexWaiters.First; - - do - { - // Skip all threads that are not waiting for this mutex. - while (currentNode != null && currentNode.Value.MutexAddress != mutexAddress) - { - currentNode = currentNode.Next; - } - - if (currentNode == null) - { - break; - } - - LinkedListNode<KThread> nextNode = currentNode.Next; - - _mutexWaiters.Remove(currentNode); - - currentNode.Value.MutexOwner = newMutexOwner; - - if (newMutexOwner != null) - { - // New owner was already selected, re-insert on new owner list. - newMutexOwner.AddToMutexWaitersList(currentNode.Value); - } - else - { - // New owner not selected yet, use current thread. - newMutexOwner = currentNode.Value; - } - - count++; - - currentNode = nextNode; - } - while (currentNode != null); - - if (newMutexOwner != null) - { - UpdatePriorityInheritance(); - - newMutexOwner.UpdatePriorityInheritance(); - } - - return newMutexOwner; - } - - private void UpdatePriorityInheritance() - { - // If any of the threads waiting for the mutex has - // higher priority than the current thread, then - // the current thread inherits that priority. - int highestPriority = BasePriority; - - if (_mutexWaiters.First != null) - { - int waitingDynamicPriority = _mutexWaiters.First.Value.DynamicPriority; - - if (waitingDynamicPriority < highestPriority) - { - highestPriority = waitingDynamicPriority; - } - } - - if (highestPriority != DynamicPriority) - { - int oldPriority = DynamicPriority; - - DynamicPriority = highestPriority; - - AdjustSchedulingForNewPriority(oldPriority); - - if (MutexOwner != null) - { - // Remove and re-insert to ensure proper sorting based on new priority. - MutexOwner._mutexWaiters.Remove(_mutexWaiterNode); - - MutexOwner.AddToMutexWaitersList(this); - - MutexOwner.UpdatePriorityInheritance(); - } - } - } - - private void AddToMutexWaitersList(KThread thread) - { - LinkedListNode<KThread> nextPrio = _mutexWaiters.First; - - int currentPriority = thread.DynamicPriority; - - while (nextPrio != null && nextPrio.Value.DynamicPriority <= currentPriority) - { - nextPrio = nextPrio.Next; - } - - if (nextPrio != null) - { - thread._mutexWaiterNode = _mutexWaiters.AddBefore(nextPrio, thread); - } - else - { - thread._mutexWaiterNode = _mutexWaiters.AddLast(thread); - } - } - - private void AdjustScheduling(ThreadSchedState oldFlags) - { - if (oldFlags == SchedFlags) - { - return; - } - - if (!IsSchedulable) - { - if (!_forcedUnschedulable) - { - // Ensure our thread is running and we have an event. - StartHostThread(); - - // If the thread is not schedulable, we want to just run or pause - // it directly as we don't care about priority or the core it is - // running on in this case. - if (SchedFlags == ThreadSchedState.Running) - { - _schedulerWaitEvent.Set(); - } - else - { - _schedulerWaitEvent.Reset(); - } - } - - return; - } - - if (oldFlags == ThreadSchedState.Running) - { - // Was running, now it's stopped. - if (ActiveCore >= 0) - { - KernelContext.PriorityQueue.Unschedule(DynamicPriority, ActiveCore, this); - } - - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0) - { - KernelContext.PriorityQueue.Unsuggest(DynamicPriority, core, this); - } - } - } - else if (SchedFlags == ThreadSchedState.Running) - { - // Was stopped, now it's running. - if (ActiveCore >= 0) - { - KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this); - } - - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0) - { - KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this); - } - } - } - - KernelContext.ThreadReselectionRequested = true; - } - - private void AdjustSchedulingForNewPriority(int oldPriority) - { - if (SchedFlags != ThreadSchedState.Running || !IsSchedulable) - { - return; - } - - // Remove thread from the old priority queues. - if (ActiveCore >= 0) - { - KernelContext.PriorityQueue.Unschedule(oldPriority, ActiveCore, this); - } - - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0) - { - KernelContext.PriorityQueue.Unsuggest(oldPriority, core, this); - } - } - - // Add thread to the new priority queues. - KThread currentThread = KernelStatic.GetCurrentThread(); - - if (ActiveCore >= 0) - { - if (currentThread == this) - { - KernelContext.PriorityQueue.SchedulePrepend(DynamicPriority, ActiveCore, this); - } - else - { - KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this); - } - } - - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0) - { - KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this); - } - } - - KernelContext.ThreadReselectionRequested = true; - } - - private void AdjustSchedulingForNewAffinity(ulong oldAffinityMask, int oldCore) - { - if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount || !IsSchedulable) - { - return; - } - - // Remove thread from the old priority queues. - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - if (((oldAffinityMask >> core) & 1) != 0) - { - if (core == oldCore) - { - KernelContext.PriorityQueue.Unschedule(DynamicPriority, core, this); - } - else - { - KernelContext.PriorityQueue.Unsuggest(DynamicPriority, core, this); - } - } - } - - // Add thread to the new priority queues. - for (int core = 0; core < KScheduler.CpuCoresCount; core++) - { - if (((AffinityMask >> core) & 1) != 0) - { - if (core == ActiveCore) - { - KernelContext.PriorityQueue.Schedule(DynamicPriority, core, this); - } - else - { - KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this); - } - } - } - - KernelContext.ThreadReselectionRequested = true; - } - - public void SetEntryArguments(long argsPtr, int threadHandle) - { - Context.SetX(0, (ulong)argsPtr); - Context.SetX(1, (ulong)threadHandle); - } - - public void TimeUp() - { - ReleaseAndResume(); - } - - public string GetGuestStackTrace() - { - return Owner.Debugger.GetGuestStackTrace(this); - } - - public string GetGuestRegisterPrintout() - { - return Owner.Debugger.GetCpuRegisterPrintout(this); - } - - public void PrintGuestStackTrace() - { - Logger.Info?.Print(LogClass.Cpu, $"Guest stack trace:\n{GetGuestStackTrace()}\n"); - } - - public void PrintGuestRegisterPrintout() - { - Logger.Info?.Print(LogClass.Cpu, $"Guest CPU registers:\n{GetGuestRegisterPrintout()}\n"); - } - - public void AddCpuTime(long ticks) - { - Interlocked.Add(ref _totalTimeRunning, ticks); - } - - public void StartHostThread() - { - if (_schedulerWaitEvent == null) - { - var schedulerWaitEvent = new ManualResetEvent(false); - - if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null) - { - HostThread.Start(); - } - else - { - schedulerWaitEvent.Dispose(); - } - } - } - - private void ThreadStart() - { - _schedulerWaitEvent.WaitOne(); - KernelStatic.SetKernelContext(KernelContext, this); - - if (_customThreadStart != null) - { - _customThreadStart(); - - // Ensure that anything trying to join the HLE thread is unblocked. - Exit(); - HandlePostSyscall(); - } - else - { - Owner.Context.Execute(Context, _entrypoint); - } - - Context.Dispose(); - _schedulerWaitEvent.Dispose(); - } - - public void MakeUnschedulable() - { - _forcedUnschedulable = true; - } - - public override bool IsSignaled() - { - return _hasExited != 0; - } - - protected override void Destroy() - { - if (_hasBeenInitialized) - { - FreeResources(); - - bool released = Owner != null || _hasBeenReleased; - - if (Owner != null) - { - Owner.ResourceLimit?.Release(LimitableResource.Thread, 1, released ? 0 : 1); - - Owner.DecrementReferenceCount(); - } - else - { - KernelContext.ResourceLimit.Release(LimitableResource.Thread, 1, released ? 0 : 1); - } - } - } - - private void FreeResources() - { - Owner?.RemoveThread(this); - - if (_tlsAddress != 0 && Owner.FreeThreadLocalStorage(_tlsAddress) != Result.Success) - { - throw new InvalidOperationException("Unexpected failure freeing thread local storage."); - } - - KernelContext.CriticalSection.Enter(); - - // Wake up all threads that may be waiting for a mutex being held by this thread. - foreach (KThread thread in _mutexWaiters) - { - thread.MutexOwner = null; - thread._originalPreferredCore = 0; - thread.ObjSyncResult = KernelResult.InvalidState; - - thread.ReleaseAndResume(); - } - - KernelContext.CriticalSection.Leave(); - - Owner?.DecrementThreadCountAndTerminateIfZero(); - } - - public void Pin() - { - IsPinned = true; - _coreMigrationDisableCount++; - - int activeCore = ActiveCore; - - _originalPreferredCore = PreferredCore; - _originalAffinityMask = AffinityMask; - - ActiveCore = CurrentCore; - PreferredCore = CurrentCore; - AffinityMask = 1UL << CurrentCore; - - if (activeCore != CurrentCore || _originalAffinityMask != AffinityMask) - { - AdjustSchedulingForNewAffinity(_originalAffinityMask, activeCore); - } - - _originalBasePriority = BasePriority; - BasePriority = Math.Min(_originalBasePriority, BitOperations.TrailingZeroCount(Owner.Capabilities.AllowedThreadPriosMask) - 1); - UpdatePriorityInheritance(); - - // Disallows thread pausing - _forcePausePermissionFlags &= ~ThreadSchedState.ThreadPauseFlag; - CombineForcePauseFlags(); - - // TODO: Assign reduced SVC permissions - } - - public void Unpin() - { - IsPinned = false; - _coreMigrationDisableCount--; - - ulong affinityMask = AffinityMask; - int activeCore = ActiveCore; - - PreferredCore = _originalPreferredCore; - AffinityMask = _originalAffinityMask; - - if (AffinityMask != affinityMask) - { - if ((AffinityMask & 1UL << ActiveCore) != 0) - { - if (PreferredCore >= 0) - { - ActiveCore = PreferredCore; - } - else - { - ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask); - } - - AdjustSchedulingForNewAffinity(affinityMask, activeCore); - } - } - - BasePriority = _originalBasePriority; - UpdatePriorityInheritance(); - - if (!TerminationRequested) - { - // Allows thread pausing - _forcePausePermissionFlags |= ThreadSchedState.ThreadPauseFlag; - CombineForcePauseFlags(); - - // TODO: Restore SVC permissions - } - - // Wake up waiters - foreach (KThread waiter in _pinnedWaiters) - { - waiter.ReleaseAndResume(); - } - - _pinnedWaiters.Clear(); - } - - public void SynchronizePreemptionState() - { - KernelContext.CriticalSection.Enter(); - - if (Owner != null && Owner.PinnedThreads[CurrentCore] == this) - { - ClearUserInterruptFlag(); - - Owner.UnpinThread(this); - } - - KernelContext.CriticalSection.Leave(); - } - - public ushort GetUserDisableCount() - { - return Owner.CpuMemory.Read<ushort>(_tlsAddress + TlsUserDisableCountOffset); - } - - public void SetUserInterruptFlag() - { - Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 1); - } - - public void ClearUserInterruptFlag() - { - Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 0); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs deleted file mode 100644 index e8ad53c2..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Ryujinx.Cpu; -using Ryujinx.Horizon.Common; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KThreadContext : IThreadContext - { - private readonly IExecutionContext _context; - - public bool Running => _context.Running; - public ulong TlsAddress => (ulong)_context.TpidrroEl0; - - public ulong GetX(int index) => _context.GetX(index); - - private int _locked; - - public KThreadContext(IExecutionContext context) - { - _context = context; - } - - public bool Lock() - { - return Interlocked.Exchange(ref _locked, 1) == 0; - } - - public void Unlock() - { - Interlocked.Exchange(ref _locked, 0); - } - } -} diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs deleted file mode 100644 index b46122be..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.Horizon.Common; - -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - class KWritableEvent : KAutoObject - { - private readonly KEvent _parent; - - public KWritableEvent(KernelContext context, KEvent parent) : base(context) - { - _parent = parent; - } - - public void Signal() - { - _parent.ReadableEvent.Signal(); - } - - public Result Clear() - { - return _parent.ReadableEvent.Clear(); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs b/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs deleted file mode 100644 index e72b719b..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/SignalType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - enum SignalType - { - Signal = 0, - SignalAndIncrementIfEqual = 1, - SignalAndModifyIfEqual = 2 - } -} diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs b/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs deleted file mode 100644 index 9577075c..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/ThreadSchedState.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - enum ThreadSchedState : ushort - { - LowMask = 0xf, - HighMask = 0xfff0, - ForcePauseMask = 0x1f0, - - ProcessPauseFlag = 1 << 4, - ThreadPauseFlag = 1 << 5, - ProcessDebugPauseFlag = 1 << 6, - BacktracePauseFlag = 1 << 7, - KernelInitPauseFlag = 1 << 8, - - None = 0, - Paused = 1, - Running = 2, - TerminationPending = 3 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs b/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs deleted file mode 100644 index 0b44b57f..00000000 --- a/Ryujinx.HLE/HOS/Kernel/Threading/ThreadType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel.Threading -{ - enum ThreadType - { - Dummy, - Kernel, - Kernel2, - User - } -}
\ No newline at end of file |
