aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS
diff options
context:
space:
mode:
authorjhorv <38920027+jhorv@users.noreply.github.com>2023-03-17 08:14:50 -0400
committerGitHub <noreply@github.com>2023-03-17 13:14:50 +0100
commit5131b71437b36f9b876046a2fddd5465652e0f10 (patch)
treea1b8647b1f948e3f88708d9ba41b2d1338f34de9 /Ryujinx.HLE/HOS
parent7870423671cba17c8aad9bb93a67c84bda441366 (diff)
Reducing memory allocations (#4537)
* add RecyclableMemoryStream dependency and MemoryStreamManager * organize BinaryReader/BinaryWriter extensions * add StreamExtensions to reduce need for BinaryWriter * simple replacments of MemoryStream with RecyclableMemoryStream * add write ReadOnlySequence<byte> support to IVirtualMemoryManager * avoid 0-length array creation * rework IpcMessage and related types to greatly reduce memory allocation by using RecylableMemoryStream, keeping streams around longer, avoiding their creation when possible, and avoiding creation of BinaryReader and BinaryWriter when possible * reduce LINQ-induced memory allocations with custom methods to query KPriorityQueue * use RecyclableMemoryStream in StreamUtils, and use StreamUtils in EmbeddedResources * add constants for nanosecond/millisecond conversions * code formatting * XML doc adjustments * fix: StreamExtension.WriteByte not writing non-zero values for lengths <= 16 * XML Doc improvements. Implement StreamExtensions.WriteByte() block writes for large-enough count values. * add copyless path for StreamExtension.Write(ReadOnlySpan<int>) * add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence<byte>); remove previous explicit implementations * code style fixes * remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator
Diffstat (limited to 'Ryujinx.HLE/HOS')
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs5
-rw-r--r--Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs5
-rw-r--r--Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs5
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs61
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcMessage.cs163
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs25
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs137
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs52
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs5
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs274
14 files changed, 435 insertions, 312 deletions
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
index 2d5509ff..952afcd5 100644
--- a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
@@ -1,5 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
using System.Collections.Generic;
@@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
private byte[] BuildResponseOld(WebCommonReturnValue result)
{
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(result);
@@ -80,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
}
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
{
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(new WebArgHeader
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
index 5cdfb314..5d5a26c2 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Hid.Types;
@@ -123,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse(ControllerSupportResultInfo result)
{
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
@@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse()
{
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)ResultCode.Success);
diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
index cec9f213..a8119a47 100644
--- a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
@@ -1,4 +1,5 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
using System.IO;
@@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Applets
{
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)PlayerSelectResult.Success);
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
index e6ed4613..c7ef7e9c 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
@@ -1,3 +1,6 @@
+using Microsoft.IO;
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
using System;
using System.IO;
@@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc
HasPId = (word & 1) != 0;
- ToCopy = new int[(word >> 1) & 0xf];
- ToMove = new int[(word >> 5) & 0xf];
-
PId = HasPId ? reader.ReadUInt64() : 0;
- for (int index = 0; index < ToCopy.Length; index++)
+ int toCopySize = (word >> 1) & 0xf;
+ int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize];
+
+ for (int index = 0; index < toCopy.Length; index++)
{
- ToCopy[index] = reader.ReadInt32();
+ toCopy[index] = reader.ReadInt32();
}
- for (int index = 0; index < ToMove.Length; index++)
+ ToCopy = toCopy;
+
+ int toMoveSize = (word >> 5) & 0xf;
+ int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize];
+
+ for (int index = 0; index < toMove.Length; index++)
{
- ToMove[index] = reader.ReadInt32();
+ toMove[index] = reader.ReadInt32();
}
+
+ ToMove = toMove;
}
public IpcHandleDesc(int[] copy, int[] move)
@@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc
return new IpcHandleDesc(Array.Empty<int>(), handles);
}
- public byte[] GetBytes()
+ public RecyclableMemoryStream GetStream()
{
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
-
- int word = HasPId ? 1 : 0;
+ RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
- word |= (ToCopy.Length & 0xf) << 1;
- word |= (ToMove.Length & 0xf) << 5;
+ int word = HasPId ? 1 : 0;
- writer.Write(word);
+ word |= (ToCopy.Length & 0xf) << 1;
+ word |= (ToMove.Length & 0xf) << 5;
- if (HasPId)
- {
- writer.Write(PId);
- }
+ ms.Write(word);
- foreach (int handle in ToCopy)
- {
- writer.Write(handle);
- }
+ if (HasPId)
+ {
+ ms.Write(PId);
+ }
- foreach (int handle in ToMove)
- {
- writer.Write(handle);
- }
+ ms.Write(ToCopy);
+ ms.Write(ToMove);
- return ms.ToArray();
- }
+ ms.Position = 0;
+ return ms;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
index 55044da4..4e8f2fbf 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
@@ -1,4 +1,8 @@
+using Microsoft.IO;
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc
ObjectIds = new List<int>();
}
- public IpcMessage(byte[] data, long cmdPtr) : this()
+ public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
{
- using (MemoryStream ms = new MemoryStream(data))
+ using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
{
BinaryReader reader = new BinaryReader(ms);
@@ -114,124 +118,119 @@ namespace Ryujinx.HLE.HOS.Ipc
for (int index = 0; index < recvListCount; index++)
{
- RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
+ RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
}
}
- public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
+ public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
{
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
+ RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
- int word0;
- int word1;
+ int word0;
+ int word1;
- word0 = (int)Type;
- word0 |= (PtrBuff.Count & 0xf) << 16;
- word0 |= (SendBuff.Count & 0xf) << 20;
- word0 |= (ReceiveBuff.Count & 0xf) << 24;
- word0 |= (ExchangeBuff.Count & 0xf) << 28;
+ word0 = (int)Type;
+ word0 |= (PtrBuff.Count & 0xf) << 16;
+ word0 |= (SendBuff.Count & 0xf) << 20;
+ word0 |= (ReceiveBuff.Count & 0xf) << 24;
+ word0 |= (ExchangeBuff.Count & 0xf) << 28;
- byte[] handleData = Array.Empty<byte>();
+ using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
- if (HandleDesc != null)
- {
- handleData = HandleDesc.GetBytes();
- }
+ int dataLength = RawData?.Length ?? 0;
- int dataLength = RawData?.Length ?? 0;
+ dataLength = (dataLength + 3) & ~3;
- dataLength = (dataLength + 3) & ~3;
+ int rawLength = dataLength;
- int rawLength = dataLength;
+ int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
- int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
+ // Apparently, padding after Raw Data is 16 bytes, however when there is
+ // padding before Raw Data too, we need to subtract the size of this padding.
+ // This is the weirdest padding I've seen so far...
+ int pad1 = 0x10 - pad0;
- // Apparently, padding after Raw Data is 16 bytes, however when there is
- // padding before Raw Data too, we need to subtract the size of this padding.
- // This is the weirdest padding I've seen so far...
- int pad1 = 0x10 - pad0;
+ dataLength = (dataLength + pad0 + pad1) / 4;
- dataLength = (dataLength + pad0 + pad1) / 4;
+ word1 = (dataLength & 0x3ff) | (2 << 10);
- word1 = (dataLength & 0x3ff) | (2 << 10);
+ if (HandleDesc != null)
+ {
+ word1 |= 1 << 31;
+ }
- if (HandleDesc != null)
- {
- word1 |= 1 << 31;
- }
+ ms.Write(word0);
+ ms.Write(word1);
- writer.Write(word0);
- writer.Write(word1);
- writer.Write(handleData);
+ if (handleDataStream != null)
+ {
+ ms.Write(handleDataStream);
+ }
- for (int index = 0; index < PtrBuff.Count; index++)
- {
- writer.Write(PtrBuff[index].GetWord0());
- writer.Write(PtrBuff[index].GetWord1());
- }
+ foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
+ {
+ ms.Write(ptrBuffDesc.GetWord0());
+ ms.Write(ptrBuffDesc.GetWord1());
+ }
- ms.Seek(pad0, SeekOrigin.Current);
+ ms.WriteByte(0, pad0);
- if (RawData != null)
- {
- writer.Write(RawData);
- ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
- }
+ if (RawData != null)
+ {
+ ms.Write(RawData);
+ ms.WriteByte(0, rawLength - RawData.Length);
+ }
- writer.Write(new byte[pad1]);
- writer.Write(recvListAddr);
+ ms.WriteByte(0, pad1);
- return ms.ToArray();
- }
+ ms.Write(recvListAddr);
+
+ ms.Position = 0;
+
+ return ms;
}
- public byte[] GetBytesTipc()
+ public RecyclableMemoryStream GetStreamTipc()
{
Debug.Assert(PtrBuff.Count == 0);
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
+ RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
- int word0;
- int word1;
+ int word0;
+ int word1;
- word0 = (int)Type;
- word0 |= (SendBuff.Count & 0xf) << 20;
- word0 |= (ReceiveBuff.Count & 0xf) << 24;
- word0 |= (ExchangeBuff.Count & 0xf) << 28;
+ word0 = (int)Type;
+ word0 |= (SendBuff.Count & 0xf) << 20;
+ word0 |= (ReceiveBuff.Count & 0xf) << 24;
+ word0 |= (ExchangeBuff.Count & 0xf) << 28;
- byte[] handleData = Array.Empty<byte>();
+ using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
- if (HandleDesc != null)
- {
- handleData = HandleDesc.GetBytes();
- }
-
- int dataLength = RawData?.Length ?? 0;
+ int dataLength = RawData?.Length ?? 0;
- dataLength = ((dataLength + 3) & ~3) / 4;
+ dataLength = ((dataLength + 3) & ~3) / 4;
- word1 = (dataLength & 0x3ff);
+ word1 = (dataLength & 0x3ff);
- if (HandleDesc != null)
- {
- word1 |= 1 << 31;
- }
+ if (HandleDesc != null)
+ {
+ word1 |= 1 << 31;
+ }
- writer.Write(word0);
- writer.Write(word1);
- writer.Write(handleData);
+ ms.Write(word0);
+ ms.Write(word1);
- if (RawData != null)
- {
- writer.Write(RawData);
- }
+ if (handleDataStream != null)
+ {
+ ms.Write(handleDataStream);
+ }
- return ms.ToArray();
+ if (RawData != null)
+ {
+ ms.Write(RawData);
}
+
+ return ms;
}
private long GetPadSize16(long position)
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
index 10406ac7..bcc9d8f8 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
@@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc
Size = size;
}
- public IpcRecvListBuffDesc(BinaryReader reader)
+ public IpcRecvListBuffDesc(ulong packedValue)
{
- ulong value = reader.ReadUInt64();
+ Position = packedValue & 0xffffffffffff;
- Position = value & 0xffffffffffff;
-
- Size = (ushort)(value >> 48);
+ Size = (ushort)(packedValue >> 48);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
index 030a314f..1af171b9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
{
- Object = schedulerObj;
+ Object = schedulerObj;
TimePoint = timePoint;
}
}
@@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private bool _keepRunning;
private long _enforceWakeupFromSpinWait;
+ private const long NanosecondsPerSecond = 1000000000L;
+ private const long NanosecondsPerMillisecond = 1000000L;
+
public KTimeManager(KernelContext context)
{
_context = context;
@@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
- if (timeout < 1000000)
+ if (timeout < NanosecondsPerMillisecond)
{
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
}
@@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private WaitingObject GetNextWaitingObject()
{
WaitingObject selected = null;
-
+
long lowestTimePoint = long.MaxValue;
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
@@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static long ConvertNanosecondsToMilliseconds(long time)
{
- time /= 1000000;
+ time /= NanosecondsPerMillisecond;
if ((ulong)time > int.MaxValue)
{
@@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static long ConvertMillisecondsToNanoseconds(long time)
{
- return time * 1000000;
+ return time * NanosecondsPerMillisecond;
}
public static long ConvertNanosecondsToHostTicks(long ns)
{
- long nsDiv = ns / 1000000000;
- long nsMod = ns % 1000000000;
- long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
- long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
+ long nsDiv = ns / NanosecondsPerSecond;
+ long nsMod = ns % NanosecondsPerSecond;
+ long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
+ long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
- long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
- return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
+ long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
+ return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
}
public static long ConvertGuestTicksToNanoseconds(long ticks)
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index eef78e18..c6467208 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KProcess currentProcess = KernelStatic.GetCurrentProcess();
- KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
+ KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
for (int index = 0; index < handles.Length; index++)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
index 2c9d7574..14fba704 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
@@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KPriorityQueue
{
- private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
- private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
+ private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
+ private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
- private long[] _scheduledPrioritiesPerCore;
- private long[] _suggestedPrioritiesPerCore;
+ private readonly long[] _scheduledPrioritiesPerCore;
+ private readonly long[] _suggestedPrioritiesPerCore;
public KPriorityQueue()
{
@@ -32,41 +32,132 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
}
- public IEnumerable<KThread> SuggestedThreads(int core)
+ public readonly ref struct KThreadEnumerable
{
- return Iterate(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
+ 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 IEnumerable<KThread> ScheduledThreads(int core)
+ public KThreadEnumerable ScheduledThreads(int core)
{
- return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
+ return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
}
- private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
+ public KThreadEnumerable SuggestedThreads(int core)
{
- long prioMask = prios[core];
-
- int prio = BitOperations.TrailingZeroCount(prioMask);
+ return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
+ }
- prioMask &= ~(1L << prio);
+ public KThread ScheduledThreadsFirstOrDefault(int core)
+ {
+ return ScheduledThreadsElementAtOrDefault(core, 0);
+ }
- while (prio < KScheduler.PrioritiesCount)
+ public KThread ScheduledThreadsElementAtOrDefault(int core, int index)
+ {
+ int currentIndex = 0;
+ foreach (var scheduledThread in ScheduledThreads(core))
{
- LinkedList<KThread> list = listPerPrioPerCore[prio][core];
+ if (currentIndex == index)
+ {
+ return scheduledThread;
+ }
+ else
+ {
+ currentIndex++;
+ }
+ }
- LinkedListNode<KThread> node = list.First;
+ return null;
+ }
- while (node != null)
+ public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority)
+ {
+ foreach (var scheduledThread in ScheduledThreads(core))
+ {
+ if (scheduledThread.DynamicPriority == dynamicPriority)
{
- yield return node.Value;
-
- node = node.Next;
+ return scheduledThread;
}
+ }
- prio = BitOperations.TrailingZeroCount(prioMask);
+ return null;
+ }
- prioMask &= ~(1L << prio);
- }
+ public bool HasScheduledThreads(int core)
+ {
+ return ScheduledThreadsFirstOrDefault(core) != null;
}
public void TransferToCore(int prio, int dstCore, KThread thread)
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index 0c51b7b9..b9de7d9c 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -1,8 +1,6 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
using System.Threading;
@@ -17,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
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;
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
for (int core = 0; core < CpuCoresCount; core++)
{
- KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
+ KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
if (thread != null &&
thread.Owner != null &&
@@ -115,12 +115,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
// 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.ScheduledThreads(core).Any())
+ if (context.PriorityQueue.HasScheduledThreads(core))
{
continue;
}
- int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
+ Array.Fill(_srcCoresHighestPrioThreads, 0);
int srcCoresHighestPrioThreadsCount = 0;
@@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
break;
}
- srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
+ _srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
}
// Not yet selected candidate found.
@@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// (the first one that doesn't make the source core idle if moved).
for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
{
- int srcCore = srcCoresHighestPrioThreads[index];
+ int srcCore = _srcCoresHighestPrioThreads[index];
- KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
+ KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1);
if (src != null)
{
@@ -422,9 +422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
{
- IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
-
- KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
+ KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio);
KThread nextThread = null;
// Yield priority queue.
@@ -433,14 +431,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
}
- IEnumerable<KThread> SuitableCandidates()
+ 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.ScheduledThreads(suggestedCore).FirstOrDefault();
+ KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
{
@@ -453,14 +451,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
nextThread == null ||
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
{
- yield return suggested;
+ 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 = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
+ KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio);
if (dst != null)
{
@@ -469,11 +472,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// 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.ScheduledThreads(core).FirstOrDefault();
+ KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
{
- dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
+ dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority);
if (dst != null)
{
@@ -534,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// Move current thread to the end of the queue.
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
- IEnumerable<KThread> SuitableCandidates()
+ static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority)
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
@@ -554,12 +557,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
suggested.DynamicPriority < nextThread.DynamicPriority)
{
- yield return suggested;
+ if (suggested.DynamicPriority <= lessThanOrEqualPriority)
+ {
+ return suggested;
+ }
}
}
+
+ return null;
}
- KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
+ KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio);
if (dst != null)
{
@@ -596,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
- if (!context.PriorityQueue.ScheduledThreads(core).Any())
+ if (!context.PriorityQueue.HasScheduledThreads(core))
{
KThread selectedThread = null;
@@ -609,7 +617,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
continue;
}
- KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
+ KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
if (firstCandidate == suggested)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
index 01b65f55..973d5f6a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
else
{
- LinkedListNode<KThread>[] syncNodes = new LinkedListNode<KThread>[syncObjs.Length];
+ LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
for (int index = 0; index < syncObjs.Length; index++)
{
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
index 227cfdae..49e342f2 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
@@ -1,4 +1,5 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
@@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
public static byte[] MakeLaunchParams(UserProfile userProfile)
{
// Size needs to be at least 0x88 bytes otherwise application errors.
- using (MemoryStream ms = new MemoryStream())
+ using (MemoryStream ms = MemoryStreamManager.Shared.GetStream())
{
BinaryWriter writer = new BinaryWriter(ms);
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
index 66a69a8b..fef82cbc 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
@@ -5,6 +5,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
+using Ryujinx.Common.Memory;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel.Memory;
@@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
static uint KXor(uint data) => data ^ FontKey;
using (BinaryReader reader = new BinaryReader(bfttfStream))
- using (MemoryStream ttfStream = new MemoryStream())
+ using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter output = new BinaryWriter(ttfStream))
{
if (KXor(reader.ReadUInt32()) != BFTTFMagic)
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index d4382a64..619f5448 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Ipc;
@@ -5,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
+using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
@@ -37,14 +40,27 @@ namespace Ryujinx.HLE.HOS.Services
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
+ private readonly MemoryStream _requestDataStream;
+ private readonly BinaryReader _requestDataReader;
+
+ private readonly MemoryStream _responseDataStream;
+ private readonly BinaryWriter _responseDataWriter;
+
public ManualResetEvent InitDone { get; }
public string Name { get; }
public Func<IpcService> SmObjectFactory { get; }
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
{
- InitDone = new ManualResetEvent(false);
_context = context;
+
+ _requestDataStream = MemoryStreamManager.Shared.GetStream();
+ _requestDataReader = new BinaryReader(_requestDataStream);
+
+ _responseDataStream = MemoryStreamManager.Shared.GetStream();
+ _responseDataWriter = new BinaryWriter(_responseDataStream);
+
+ InitDone = new ManualResetEvent(false);
Name = name;
SmObjectFactory = smObjectFactory;
@@ -110,15 +126,15 @@ namespace Ryujinx.HLE.HOS.Services
while (true)
{
- int[] portHandles = _portHandles.ToArray();
- int[] sessionHandles = _sessionHandles.ToArray();
- int[] handles = new int[portHandles.Length + sessionHandles.Length];
+ int handleCount = _portHandles.Count + _sessionHandles.Count;
- portHandles.CopyTo(handles, 0);
- sessionHandles.CopyTo(handles, portHandles.Length);
+ int[] handles = ArrayPool<int>.Shared.Rent(handleCount);
+
+ _portHandles.CopyTo(handles, 0);
+ _sessionHandles.CopyTo(handles, _portHandles.Count);
// We still need a timeout here to allow the service to pick up and listen new sessions...
- var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
+ var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
thread.HandlePostSyscall();
@@ -129,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services
replyTargetHandle = 0;
- if (rc == Result.Success && signaledIndex >= portHandles.Length)
+ if (rc == Result.Success && signaledIndex >= _portHandles.Count)
{
// We got a IPC request, process it, pass to the appropriate service if needed.
int signaledHandle = handles[signaledIndex];
@@ -156,6 +172,8 @@ namespace Ryujinx.HLE.HOS.Services
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
}
+
+ ArrayPool<int>.Shared.Return(handles);
}
Dispose();
@@ -166,13 +184,9 @@ namespace Ryujinx.HLE.HOS.Services
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress;
- ulong messageSize = 0x100;
- byte[] reqData = new byte[messageSize];
+ IpcMessage request = ReadRequest(process, messagePtr);
- process.CpuMemory.Read(messagePtr, reqData);
-
- IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
IpcMessage response = new IpcMessage();
ulong tempAddr = recvListAddr;
@@ -202,158 +216,157 @@ namespace Ryujinx.HLE.HOS.Services
bool shouldReply = true;
bool isTipcCommunication = false;
- using (MemoryStream raw = new MemoryStream(request.RawData))
+ _requestDataStream.SetLength(0);
+ _requestDataStream.Write(request.RawData);
+ _requestDataStream.Position = 0;
+
+ if (request.Type == IpcMessageType.HipcRequest ||
+ request.Type == IpcMessageType.HipcRequestWithContext)
{
- BinaryReader reqReader = new BinaryReader(raw);
+ response.Type = IpcMessageType.HipcResponse;
- if (request.Type == IpcMessageType.HipcRequest ||
- request.Type == IpcMessageType.HipcRequestWithContext)
- {
- response.Type = IpcMessageType.HipcResponse;
+ _responseDataStream.SetLength(0);
- using (MemoryStream resMs = new MemoryStream())
- {
- BinaryWriter resWriter = new BinaryWriter(resMs);
+ ServiceCtx context = new ServiceCtx(
+ _context.Device,
+ process,
+ process.CpuMemory,
+ thread,
+ request,
+ response,
+ _requestDataReader,
+ _responseDataWriter);
- ServiceCtx context = new ServiceCtx(
- _context.Device,
- process,
- process.CpuMemory,
- thread,
- request,
- response,
- reqReader,
- resWriter);
+ _sessions[serverSessionHandle].CallHipcMethod(context);
- _sessions[serverSessionHandle].CallHipcMethod(context);
+ response.RawData = _responseDataStream.ToArray();
+ }
+ else if (request.Type == IpcMessageType.HipcControl ||
+ request.Type == IpcMessageType.HipcControlWithContext)
+ {
+ uint magic = (uint)_requestDataReader.ReadUInt64();
+ uint cmdId = (uint)_requestDataReader.ReadUInt64();
- response.RawData = resMs.ToArray();
- }
- }
- else if (request.Type == IpcMessageType.HipcControl ||
- request.Type == IpcMessageType.HipcControlWithContext)
+ switch (cmdId)
{
- uint magic = (uint)reqReader.ReadUInt64();
- uint cmdId = (uint)reqReader.ReadUInt64();
+ case 0:
+ FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
+ break;
- switch (cmdId)
- {
- case 0:
- request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
- break;
-
- case 3:
- request = FillResponse(response, 0, PointerBufferSize);
- break;
+ case 3:
+ FillHipcResponse(response, 0, PointerBufferSize);
+ break;
- // TODO: Whats the difference between IpcDuplicateSession/Ex?
- case 2:
- case 4:
- int unknown = reqReader.ReadInt32();
+ // TODO: Whats the difference between IpcDuplicateSession/Ex?
+ case 2:
+ case 4:
+ int unknown = _requestDataReader.ReadInt32();
- _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
+ _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
- AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
+ AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
- response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
+ response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
- request = FillResponse(response, 0);
+ FillHipcResponse(response, 0);
- break;
+ break;
- default: throw new NotImplementedException(cmdId.ToString());
- }
- }
- else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
- {
- _context.Syscall.CloseHandle(serverSessionHandle);
- _sessionHandles.Remove(serverSessionHandle);
- IpcService service = _sessions[serverSessionHandle];
- if (service is IDisposable disposableObj)
- {
- disposableObj.Dispose();
- }
- _sessions.Remove(serverSessionHandle);
- shouldReply = false;
+ default: throw new NotImplementedException(cmdId.ToString());
}
- // If the type is past 0xF, we are using TIPC
- else if (request.Type > IpcMessageType.TipcCloseSession)
- {
- isTipcCommunication = true;
-
- // Response type is always the same as request on TIPC.
- response.Type = request.Type;
+ }
+ else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
+ {
+ _context.Syscall.CloseHandle(serverSessionHandle);
+ _sessionHandles.Remove(serverSessionHandle);
+ IpcService service = _sessions[serverSessionHandle];
+ (service as IDisposable)?.Dispose();
+ _sessions.Remove(serverSessionHandle);
+ shouldReply = false;
+ }
+ // If the type is past 0xF, we are using TIPC
+ else if (request.Type > IpcMessageType.TipcCloseSession)
+ {
+ isTipcCommunication = true;
- using (MemoryStream resMs = new MemoryStream())
- {
- BinaryWriter resWriter = new BinaryWriter(resMs);
+ // Response type is always the same as request on TIPC.
+ response.Type = request.Type;
- ServiceCtx context = new ServiceCtx(
- _context.Device,
- process,
- process.CpuMemory,
- thread,
- request,
- response,
- reqReader,
- resWriter);
+ _responseDataStream.SetLength(0);
- _sessions[serverSessionHandle].CallTipcMethod(context);
+ ServiceCtx context = new ServiceCtx(
+ _context.Device,
+ process,
+ process.CpuMemory,
+ thread,
+ request,
+ response,
+ _requestDataReader,
+ _responseDataWriter);
- response.RawData = resMs.ToArray();
- }
+ _sessions[serverSessionHandle].CallTipcMethod(context);
- process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
- }
- else
- {
- throw new NotImplementedException(request.Type.ToString());
- }
+ response.RawData = _responseDataStream.ToArray();
- if (!isTipcCommunication)
- {
- process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
- }
+ using var responseStream = response.GetStreamTipc();
+ process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
+ }
+ else
+ {
+ throw new NotImplementedException(request.Type.ToString());
+ }
- return shouldReply;
+ if (!isTipcCommunication)
+ {
+ using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48));
+ process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
}
+
+ return shouldReply;
}
- private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
+ private static IpcMessage ReadRequest(KProcess process, ulong messagePtr)
{
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
+ const int messageSize = 0x100;
- foreach (int value in values)
- {
- writer.Write(value);
- }
+ byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
- return FillResponse(response, result, ms.ToArray());
- }
+ Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize);
+ reqDataSpan.Clear();
+
+ process.CpuMemory.Read(messagePtr, reqDataSpan);
+
+ IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr);
+
+ ArrayPool<byte>.Shared.Return(reqData);
+
+ return request;
}
- private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
+ private void FillHipcResponse(IpcMessage response, long result)
{
- response.Type = IpcMessageType.HipcResponse;
+ FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty);
+ }
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
+ private void FillHipcResponse(IpcMessage response, long result, int value)
+ {
+ Span<byte> span = stackalloc byte[sizeof(int)];
+ BinaryPrimitives.WriteInt32LittleEndian(span, value);
+ FillHipcResponse(response, result, span);
+ }
- writer.Write(IpcMagic.Sfco);
- writer.Write(result);
+ private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data)
+ {
+ response.Type = IpcMessageType.HipcResponse;
- if (data != null)
- {
- writer.Write(data);
- }
+ _responseDataStream.SetLength(0);
- response.RawData = ms.ToArray();
- }
+ _responseDataStream.Write(IpcMagic.Sfco);
+ _responseDataStream.Write(result);
- return response;
+ _responseDataStream.Write(data);
+
+ response.RawData = _responseDataStream.ToArray();
}
protected virtual void Dispose(bool disposing)
@@ -372,6 +385,11 @@ namespace Ryujinx.HLE.HOS.Services
_sessions.Clear();
+ _requestDataReader.Dispose();
+ _requestDataStream.Dispose();
+ _responseDataWriter.Dispose();
+ _responseDataStream.Dispose();
+
InitDone.Dispose();
}
}