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
This commit is contained in:
parent
5dd6f41ff4
commit
6c9565693f
18 changed files with 138 additions and 135 deletions
|
@ -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>)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -15,13 +15,13 @@ namespace Ryujinx.HLE.HOS.Services
|
|||
{
|
||||
public IReadOnlyDictionary<int, MethodInfo> Commands { get; }
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
177
Ryujinx.HLE/HOS/Services/ServerBase.cs
Normal file
177
Ryujinx.HLE/HOS/Services/ServerBase.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue