aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services/ServerBase.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-09-22 01:50:40 -0300
committerGitHub <noreply@github.com>2020-09-22 14:50:40 +1000
commit6c9565693fd87ae1af81ed63b5fbdde2a5dbecb8 (patch)
treef7d87a5e4aff1b3f0b446bbcc710fcb89ffc288f /Ryujinx.HLE/HOS/Services/ServerBase.cs
parent5dd6f41ff456c2d9a72d9e6d88c4be851bac1f96 (diff)
IPC refactor part 1: Use explicit separate threads to process requests (#1447)
* Changes to allow explicit management of service threads * Remove now unused code * Remove ThreadCounter, its no longer needed * Allow and use separate server per service, also fix exit issues * New policy change: PTC version now uses PR number
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/ServerBase.cs')
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs177
1 files changed, 177 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
new file mode 100644
index 00000000..211e0e6b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -0,0 +1,177 @@
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.HOS.Services
+{
+ class ServerBase
+ {
+ private struct IpcRequest
+ {
+ public Switch Device { get; }
+ public KProcess Process => Thread?.Owner;
+ public KThread Thread { get; }
+ public KClientSession Session { get; }
+ public ulong MessagePtr { get; }
+ public ulong MessageSize { get; }
+
+ public IpcRequest(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
+ {
+ Device = device;
+ Thread = thread;
+ Session = session;
+ MessagePtr = messagePtr;
+ MessageSize = messageSize;
+ }
+
+ public void SignalDone(KernelResult result)
+ {
+ Thread.ObjSyncResult = result;
+ Thread.Reschedule(ThreadSchedState.Running);
+ }
+ }
+
+ private readonly AsyncWorkQueue<IpcRequest> _ipcProcessor;
+
+ public ServerBase(string name)
+ {
+ _ipcProcessor = new AsyncWorkQueue<IpcRequest>(Process, name);
+ }
+
+ public void PushMessage(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
+ {
+ _ipcProcessor.Add(new IpcRequest(device, thread, session, messagePtr, messageSize));
+ }
+
+ private void Process(IpcRequest message)
+ {
+ byte[] reqData = new byte[message.MessageSize];
+
+ message.Process.CpuMemory.Read(message.MessagePtr, reqData);
+
+ IpcMessage request = new IpcMessage(reqData, (long)message.MessagePtr);
+ IpcMessage response = new IpcMessage();
+
+ using (MemoryStream raw = new MemoryStream(request.RawData))
+ {
+ BinaryReader reqReader = new BinaryReader(raw);
+
+ if (request.Type == IpcMessageType.Request ||
+ request.Type == IpcMessageType.RequestWithContext)
+ {
+ response.Type = IpcMessageType.Response;
+
+ using (MemoryStream resMs = new MemoryStream())
+ {
+ BinaryWriter resWriter = new BinaryWriter(resMs);
+
+ ServiceCtx context = new ServiceCtx(
+ message.Device,
+ message.Process,
+ message.Process.CpuMemory,
+ message.Thread,
+ message.Session,
+ request,
+ response,
+ reqReader,
+ resWriter);
+
+ message.Session.Service.CallMethod(context);
+
+ response.RawData = resMs.ToArray();
+ }
+ }
+ else if (request.Type == IpcMessageType.Control ||
+ request.Type == IpcMessageType.ControlWithContext)
+ {
+ uint magic = (uint)reqReader.ReadUInt64();
+ uint cmdId = (uint)reqReader.ReadUInt64();
+
+ switch (cmdId)
+ {
+ case 0:
+ request = FillResponse(response, 0, message.Session.Service.ConvertToDomain());
+ break;
+
+ case 3:
+ request = FillResponse(response, 0, 0x1000);
+ break;
+
+ // TODO: Whats the difference between IpcDuplicateSession/Ex?
+ case 2:
+ case 4:
+ int unknown = reqReader.ReadInt32();
+
+ if (message.Process.HandleTable.GenerateHandle(message.Session, out int handle) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ response.HandleDesc = IpcHandleDesc.MakeMove(handle);
+
+ request = FillResponse(response, 0);
+
+ break;
+
+ default: throw new NotImplementedException(cmdId.ToString());
+ }
+ }
+ else if (request.Type == IpcMessageType.CloseSession)
+ {
+ message.SignalDone(KernelResult.PortRemoteClosed);
+ return;
+ }
+ else
+ {
+ throw new NotImplementedException(request.Type.ToString());
+ }
+
+ message.Process.CpuMemory.Write(message.MessagePtr, response.GetBytes((long)message.MessagePtr));
+ }
+
+ message.SignalDone(KernelResult.Success);
+ }
+
+ private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
+ {
+ using (MemoryStream ms = new MemoryStream())
+ {
+ BinaryWriter writer = new BinaryWriter(ms);
+
+ foreach (int value in values)
+ {
+ writer.Write(value);
+ }
+
+ return FillResponse(response, result, ms.ToArray());
+ }
+ }
+
+ private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
+ {
+ response.Type = IpcMessageType.Response;
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ BinaryWriter writer = new BinaryWriter(ms);
+
+ writer.Write(IpcMagic.Sfco);
+ writer.Write(result);
+
+ if (data != null)
+ {
+ writer.Write(data);
+ }
+
+ response.RawData = ms.ToArray();
+ }
+
+ return response;
+ }
+ }
+} \ No newline at end of file