aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services
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
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')
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/CommandAttributes.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/IpcService.cs23
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs177
-rw-r--r--Ryujinx.HLE/HOS/Services/ServiceAttributes.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs2
9 files changed, 217 insertions, 14 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
index ae2b9802..7c27be61 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
private const int DefaultSampleRate = 48000;
private const int DefaultChannelsCount = 2;
- public IAudioOutManager(ServiceCtx context) { }
+ public IAudioOutManager(ServiceCtx context) : base(new ServerBase("AudioOutServer")) { }
[Command(0)]
// ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
diff --git a/Ryujinx.HLE/HOS/Services/CommandAttributes.cs b/Ryujinx.HLE/HOS/Services/CommandAttributes.cs
index 2747552b..43aadf16 100644
--- a/Ryujinx.HLE/HOS/Services/CommandAttributes.cs
+++ b/Ryujinx.HLE/HOS/Services/CommandAttributes.cs
@@ -3,7 +3,7 @@
namespace Ryujinx.HLE.HOS.Services
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
- public class CommandAttribute : Attribute
+ class CommandAttribute : Attribute
{
public readonly int Id;
diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs
index cff1b816..9d40d80f 100644
--- a/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -15,13 +15,13 @@ namespace Ryujinx.HLE.HOS.Services
{
public IReadOnlyDictionary<int, MethodInfo> Commands { get; }
- private IdDictionary _domainObjects;
+ public ServerBase Server { get; private set; }
+ private IdDictionary _domainObjects;
private int _selfId;
-
private bool _isDomain;
- public IpcService()
+ public IpcService(ServerBase server = null)
{
Commands = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => type == GetType())
@@ -30,8 +30,9 @@ namespace Ryujinx.HLE.HOS.Services
.Select(command => (((CommandAttribute)command).Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo);
- _domainObjects = new IdDictionary();
+ Server = server;
+ _domainObjects = new IdDictionary();
_selfId = -1;
}
@@ -152,6 +153,8 @@ namespace Ryujinx.HLE.HOS.Services
{
IpcService service = context.Session.Service;
+ obj.TrySetServer(service.Server);
+
if (service._isDomain)
{
context.Response.ObjectIds.Add(service.Add(obj));
@@ -194,6 +197,18 @@ namespace Ryujinx.HLE.HOS.Services
return obj is T ? (T)obj : null;
}
+ public bool TrySetServer(ServerBase newServer)
+ {
+ if (Server == null)
+ {
+ Server = newServer;
+
+ return true;
+ }
+
+ return false;
+ }
+
private int Add(IIpcService obj)
{
return _domainObjects.Add(obj);
diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
index 23253cf1..0812683b 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
@@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
private bool _transferMemInitialized = false;
- public INvDrvServices(ServiceCtx context)
+ public INvDrvServices(ServiceCtx context) : base(new ServerBase("NvservicesServer"))
{
_owner = null;
}
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
diff --git a/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs b/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs
index 9ca775af..1b896a27 100644
--- a/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs
+++ b/Ryujinx.HLE/HOS/Services/ServiceAttributes.cs
@@ -3,7 +3,7 @@
namespace Ryujinx.HLE.HOS.Services
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public class ServiceAttribute : Attribute
+ class ServiceAttribute : Attribute
{
public readonly string Name;
public readonly object Parameter;
diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index 37f0a23c..d8f31faf 100644
--- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -18,9 +18,11 @@ namespace Ryujinx.HLE.HOS.Services.Sm
private ConcurrentDictionary<string, KPort> _registeredServices;
+ private readonly ServerBase _commonServer;
+
private bool _isInitialized;
- public IUserInterface(ServiceCtx context = null)
+ public IUserInterface(ServiceCtx context = null) : base(new ServerBase("SmServer"))
{
_registeredServices = new ConcurrentDictionary<string, KPort>();
@@ -28,6 +30,8 @@ namespace Ryujinx.HLE.HOS.Services.Sm
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
.Select(service => (((ServiceAttribute)service).Name, type)))
.ToDictionary(service => service.Name, service => service.type);
+
+ _commonServer = new ServerBase("CommonServer");
}
public static void InitializePort(Horizon system)
@@ -36,7 +40,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm
port.ClientPort.SetName("sm:");
- port.ClientPort.Service = new IUserInterface();
+ IUserInterface smService = new IUserInterface();
+
+ port.ClientPort.Service = smService;
}
[Command(0)]
@@ -81,8 +87,13 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name);
- session.ClientSession.Service = serviceAttribute.Parameter != null ? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter)
- : (IpcService)Activator.CreateInstance(type, context);
+ IpcService service = serviceAttribute.Parameter != null
+ ? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter)
+ : (IpcService)Activator.CreateInstance(type, context);
+
+ service.TrySetServer(_commonServer);
+
+ session.ClientSession.Service = service;
}
else
{
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
index adeecb66..4ed62128 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private List<BsdSocket> _sockets = new List<BsdSocket>();
- public IClient(ServiceCtx context, bool isPrivileged)
+ public IClient(ServiceCtx context, bool isPrivileged) : base(new ServerBase("BsdServer"))
{
_isPrivileged = isPrivileged;
}
diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
index dbadd90b..3e853f02 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
[Service("vi:u")]
class IApplicationRootService : IpcService
{
- public IApplicationRootService(ServiceCtx context) { }
+ public IApplicationRootService(ServiceCtx context) : base(new ServerBase("ViServer")) { }
[Command(0)]
// GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>