From 5e67569a6ec668bf070353a372384f12a64c5f07 Mon Sep 17 00:00:00 2001 From: avan Date: Sat, 1 Aug 2026 22:18:02 +0800 Subject: [PATCH] Fix a hang during the loading stage In KAddressArbiter, threads were originally inserted into the wait queue according to their DynamicPriority. When multiple threads had the same DynamicPriority, the original implementation could not guarantee their existing order. Threads with the same dynamic priority are now inserted in FIFO order, preventing unstable wake-up ordering from blocking the guest synchronization flow. (cherry picked from commit ef14467d1f0478f1b8f77cf08ed8f7af58e1f74a) --- .../HOS/Kernel/Threading/KAddressArbiter.cs | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index 8b339eabc..97f28bb70 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -15,7 +15,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private readonly Dictionary> _condVarThreads; private readonly Dictionary> _arbiterThreads; - private readonly ByDynamicPriority _byDynamicPriority; public KAddressArbiter(KernelContext context) { @@ -23,7 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _condVarThreads = []; _arbiterThreads = []; - _byDynamicPriority = new ByDynamicPriority(); } public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) @@ -142,14 +140,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (_condVarThreads.TryGetValue(condVarAddress, out List threads)) { - int i = 0; + int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread); + - if (threads.Count > 0) - { - i = threads.BinarySearch(currentThread, _byDynamicPriority); - if (i < 0) i = ~i; - } - threads.Insert(i, currentThread); } else @@ -332,14 +325,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (_arbiterThreads.TryGetValue(address, out List threads)) { - int i = 0; + int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread); + - if (threads.Count > 0) - { - i = threads.BinarySearch(currentThread, _byDynamicPriority); - if (i < 0) i = ~i; - } - threads.Insert(i, currentThread); } else @@ -424,14 +412,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (_arbiterThreads.TryGetValue(address, out List threads)) { - int i = 0; + int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread); + - if (threads.Count > 0) - { - i = threads.BinarySearch(currentThread, _byDynamicPriority); - if (i < 0) i = ~i; - } - threads.Insert(i, currentThread); } else @@ -627,12 +610,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading return validCount; } - private class ByDynamicPriority : IComparer + private static int FindDynamicPriorityFifoInsertionIndex(List threads, KThread currentThread) { - public int Compare(KThread x, KThread y) + int low = 0; + int high = threads.Count; + + // Lower numeric values represent higher priorities. Use upper-bound insertion + // to preserve FIFO order among waiters with the same dynamic priority. + while (low < high) { - return x!.DynamicPriority.CompareTo(y!.DynamicPriority); + int middle = low + ((high - low) >> 1); + + if (threads[middle].DynamicPriority <= currentThread.DynamicPriority) + { + low = middle + 1; + } + else + { + high = middle; + } } + + return low; } } }