diff options
author | gdkchan <gab.dark.100@gmail.com> | 2020-09-22 01:50:40 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-22 14:50:40 +1000 |
commit | 6c9565693fd87ae1af81ed63b5fbdde2a5dbecb8 (patch) | |
tree | f7d87a5e4aff1b3f0b446bbcc710fcb89ffc288f /Ryujinx.HLE/HOS/Services | |
parent | 5dd6f41ff456c2d9a72d9e6d88c4be851bac1f96 (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.cs | 2 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/CommandAttributes.cs | 2 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/IpcService.cs | 23 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 2 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/ServerBase.cs | 177 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/ServiceAttributes.cs | 2 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 19 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs | 2 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs | 2 |
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> |