See merge request ryubing/ryujinx!234
This commit is contained in:
LotP
2025-12-06 17:19:19 -06:00
committed by GreemDev
parent fd7554425a
commit c3155fcadb
37 changed files with 563 additions and 677 deletions

View File

@@ -20,6 +20,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
_exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
}
public KBufferDescriptorTable Clear()
{
_sendBufferDescriptors.Clear();
_receiveBufferDescriptors.Clear();
_exchangeBufferDescriptors.Clear();
return this;
}
public Result AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
{
return Add(_sendBufferDescriptors, src, dst, size, state);

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
@@ -32,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize);
KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize);
KernelContext.CriticalSection.Enter();
@@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
KThread currentThread = KernelStatic.GetCurrentThread();
KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
KernelContext.CriticalSection.Enter();

View File

@@ -10,6 +10,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KServerSession : KSynchronizationObject
{
public readonly ObjectPool<KSessionRequest> RequestPool = new(() => new KSessionRequest());
private static readonly MemoryState[] _ipcMemoryStates =
[
MemoryState.IpcBuffer3,
@@ -274,6 +276,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KernelContext.CriticalSection.Leave();
WakeClientThread(request, clientResult);
RequestPool.Release(request);
}
if (clientHeader.ReceiveListType < 2 &&
@@ -627,6 +631,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
CloseAllHandles(clientMsg, serverHeader, clientProcess);
FinishRequest(request, clientResult);
RequestPool.Release(request);
}
if (clientHeader.ReceiveListType < 2 &&
@@ -865,6 +871,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
// Unmap buffers from server.
FinishRequest(request, clientResult);
RequestPool.Release(request);
return serverResult;
}
@@ -1098,6 +1106,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
{
FinishRequest(request, KernelResult.PortRemoteClosed);
RequestPool.Release(request);
}
}
@@ -1117,6 +1127,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
}
RequestPool.Release(request);
}
WakeServerThreads(KernelResult.PortRemoteClosed);

View File

@@ -5,18 +5,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KSessionRequest
{
public KBufferDescriptorTable BufferDescriptorTable { get; }
public KBufferDescriptorTable BufferDescriptorTable { get; private set; }
public KThread ClientThread { get; }
public KThread ClientThread { get; private set; }
public KProcess ServerProcess { get; set; }
public KWritableEvent AsyncEvent { get; }
public KWritableEvent AsyncEvent { get; private set; }
public ulong CustomCmdBuffAddr { get; }
public ulong CustomCmdBuffSize { get; }
public ulong CustomCmdBuffAddr { get; private set; }
public ulong CustomCmdBuffSize { get; private set; }
public KSessionRequest(
public KSessionRequest Set(
KThread clientThread,
ulong customCmdBuffAddr,
ulong customCmdBuffSize,
@@ -27,7 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
CustomCmdBuffSize = customCmdBuffSize;
AsyncEvent = asyncEvent;
BufferDescriptorTable = new KBufferDescriptorTable();
BufferDescriptorTable = BufferDescriptorTable?.Clear() ?? new KBufferDescriptorTable();
return this;
}
}
}

View File

@@ -1,10 +1,8 @@
using Ryujinx.Common;
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
@@ -12,12 +10,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KAddressArbiter
{
private const int HasListenersMask = 0x40000000;
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
private readonly KernelContext _context;
private readonly List<KThread> _condVarThreads;
private readonly List<KThread> _arbiterThreads;
private readonly Dictionary<ulong, List<KThread>> _condVarThreads;
private readonly Dictionary<ulong, List<KThread>> _arbiterThreads;
private readonly ByDynamicPriority _byDynamicPriority;
public KAddressArbiter(KernelContext context)
{
@@ -25,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_condVarThreads = [];
_arbiterThreads = [];
_byDynamicPriority = new ByDynamicPriority();
}
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
@@ -140,9 +139,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexAddress = mutexAddress;
currentThread.ThreadHandleForUserMutex = threadHandle;
currentThread.CondVarAddress = condVarAddress;
_condVarThreads.Add(currentThread);
if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads))
{
int i = 0;
if (threads.Count > 0)
{
i = threads.BinarySearch(currentThread, _byDynamicPriority);
if (i < 0) i = ~i;
}
threads.Insert(i, currentThread);
}
else
{
_condVarThreads.Add(condVarAddress, [currentThread]);
}
if (timeout != 0)
{
@@ -165,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexOwner?.RemoveMutexWaiter(currentThread);
_condVarThreads.Remove(currentThread);
_condVarThreads[condVarAddress].Remove(currentThread);
_context.CriticalSection.Leave();
@@ -200,13 +213,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_context.CriticalSection.Enter();
static bool SignalProcessWideKeyPredicate(KThread thread, ulong address)
int validThreads = 0;
_condVarThreads.TryGetValue(address, out List<KThread> threads);
if (threads is not null && threads.Count > 0)
{
return thread.CondVarAddress == address;
validThreads = WakeThreads(threads, count, TryAcquireMutex);
}
int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address);
if (validThreads == 0)
{
KernelTransfer.KernelToUser(address, 0);
@@ -315,9 +329,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexAddress = address;
currentThread.WaitingInArbitration = true;
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
{
int i = 0;
_arbiterThreads.Add(currentThread);
if (threads.Count > 0)
{
i = threads.BinarySearch(currentThread, _byDynamicPriority);
if (i < 0) i = ~i;
}
threads.Insert(i, currentThread);
}
else
{
_arbiterThreads.Add(address, [currentThread]);
}
currentThread.Reschedule(ThreadSchedState.Paused);
if (timeout > 0)
@@ -336,7 +365,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (currentThread.WaitingInArbitration)
{
_arbiterThreads.Remove(currentThread);
_arbiterThreads[address].Remove(currentThread);
currentThread.WaitingInArbitration = false;
}
@@ -392,9 +421,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.MutexAddress = address;
currentThread.WaitingInArbitration = true;
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
{
int i = 0;
_arbiterThreads.Add(currentThread);
if (threads.Count > 0)
{
i = threads.BinarySearch(currentThread, _byDynamicPriority);
if (i < 0) i = ~i;
}
threads.Insert(i, currentThread);
}
else
{
_arbiterThreads.Add(address, [currentThread]);
}
currentThread.Reschedule(ThreadSchedState.Paused);
if (timeout > 0)
@@ -413,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (currentThread.WaitingInArbitration)
{
_arbiterThreads.Remove(currentThread);
_arbiterThreads[address].Remove(currentThread);
currentThread.WaitingInArbitration = false;
}
@@ -485,16 +529,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// 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)
{
if (thread.MutexAddress == address &&
++waitingCount >= count)
{
break;
}
}
int waitingCount = _arbiterThreads[address].Count;
if (waitingCount > 0)
{
@@ -561,55 +596,38 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
thread.WaitingInArbitration = false;
}
static bool ArbiterThreadPredecate(KThread thread, ulong address)
{
return thread.MutexAddress == address;
}
_arbiterThreads.TryGetValue(address, out List<KThread> threads);
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address);
if (threads is not null && threads.Count > 0)
{
WakeThreads(threads, count, RemoveArbiterThread);
}
}
private static int WakeThreads(
List<KThread> threads,
int count,
Action<KThread> removeCallback,
Func<KThread, ulong, bool> predicate,
ulong address = 0)
Action<KThread> removeCallback)
{
KThread[] candidates = _threadArrayPool.Allocate();
if (candidates.Length < threads.Count)
{
Array.Resize(ref candidates, threads.Count);
}
int validCount = 0;
for (int i = 0; i < threads.Count; i++)
{
if (predicate(threads[i], address))
{
candidates[validCount++] = threads[i];
}
}
Span<KThread> candidatesSpan = candidates.AsSpan(..validCount);
candidatesSpan.Sort((x, y) => (x.DynamicPriority.CompareTo(y.DynamicPriority)));
int validCount = count > 0 ? Math.Min(count, threads.Count) : threads.Count;
if (count > 0)
{
candidatesSpan = candidatesSpan[..Math.Min(count, candidatesSpan.Length)];
}
foreach (KThread thread in candidatesSpan)
for (int i = 0; i < validCount; i++)
{
KThread thread = threads[i];
removeCallback(thread);
threads.Remove(thread);
}
_threadArrayPool.Release(candidates);
threads.RemoveRange(0, validCount);
return validCount;
}
private class ByDynamicPriority : IComparer<KThread>
{
public int Compare(KThread x, KThread y)
{
return x!.DynamicPriority.CompareTo(y!.DynamicPriority);
}
}
}
}

View File

@@ -61,8 +61,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KSynchronizationObject SignaledObj { get; set; }
public ulong CondVarAddress { get; set; }
private ulong _entrypoint;
private ThreadStart _customThreadStart;
private bool _forcedUnschedulable;