Jelajahi Sumber

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
gdkchan 5 tahun lalu
induk
melakukan
6c9565693f

+ 2 - 1
ARMeilleure/Instructions/InstEmitException.cs

@@ -1,7 +1,6 @@
 using ARMeilleure.Decoders;
 using ARMeilleure.Translation;
 
-using static ARMeilleure.Instructions.InstEmitFlowHelper;
 using static ARMeilleure.IntermediateRepresentation.OperandHelper;
 
 namespace ARMeilleure.Instructions
@@ -27,6 +26,8 @@ namespace ARMeilleure.Instructions
             context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
 
             context.LoadFromContext();
+
+            Translator.EmitSynchronization(context);
         }
 
         public static void Und(ArmEmitterContext context)

+ 2 - 0
ARMeilleure/Instructions/InstEmitException32.cs

@@ -27,6 +27,8 @@ namespace ARMeilleure.Instructions
             context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
 
             context.LoadFromContext();
+
+            Translator.EmitSynchronization(context);
         }
     }
 }

+ 1 - 1
ARMeilleure/Translation/PTC/Ptc.cs

@@ -21,7 +21,7 @@ namespace ARMeilleure.Translation.PTC
     {
         private const string HeaderMagic = "PTChd";
 
-        private const int InternalVersion = 1528; //! To be incremented manually for each change to the ARMeilleure project.
+        private const int InternalVersion = 1447; //! To be incremented manually for each change to the ARMeilleure project.
 
         private const string ActualDir = "0";
         private const string BackupDir = "1";

+ 1 - 1
ARMeilleure/Translation/Translator.cs

@@ -290,7 +290,7 @@ namespace ARMeilleure.Translation
             return context.GetControlFlowGraph();
         }
 
-        private static void EmitSynchronization(EmitterContext context)
+        internal static void EmitSynchronization(EmitterContext context)
         {
             long countOffs = NativeContext.GetCounterOffset();
 

+ 11 - 10
Ryujinx.HLE/HOS/Horizon.cs

@@ -30,7 +30,7 @@ using Ryujinx.HLE.Loaders.Executables;
 using Ryujinx.HLE.Utilities;
 using System;
 using System.IO;
-
+using System.Threading;
 
 namespace Ryujinx.HLE.HOS
 {
@@ -147,7 +147,7 @@ namespace Ryujinx.HLE.HOS
 
             // Configure and setup internal offset
             TimeSpanType internalOffset = TimeSpanType.FromSeconds(ConfigurationState.Instance.System.SystemTimeOffset);
-            
+
             TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds);
 
             if (systemTime.IsDaylightSavingTime() && !systemTimeOffset.IsDaylightSavingTime())
@@ -318,18 +318,19 @@ namespace Ryujinx.HLE.HOS
 
                 terminationThread.Start();
 
+                // Wait until the thread is actually started.
+                while (terminationThread.HostThread.ThreadState == ThreadState.Unstarted)
+                {
+                    Thread.Sleep(10);
+                }
+
+                // Wait until the termination thread is done terminating all the other threads.
+                terminationThread.HostThread.Join();
+
                 // Destroy nvservices channels as KThread could be waiting on some user events.
                 // This is safe as KThread that are likely to call ioctls are going to be terminated by the post handler hook on the SVC facade.
                 INvDrvServices.Destroy();
 
-                // This is needed as the IPC Dummy KThread is also counted in the ThreadCounter.
-                KernelContext.ThreadCounter.Signal();
-
-                // It's only safe to release resources once all threads
-                // have exited.
-                KernelContext.ThreadCounter.Signal();
-                KernelContext.ThreadCounter.Wait();
-
                 AudioRendererManager.Dispose();
 
                 KernelContext.Dispose();

+ 0 - 4
Ryujinx.HLE/HOS/Kernel/KernelContext.cs

@@ -24,8 +24,6 @@ namespace Ryujinx.HLE.HOS.Kernel
         public Syscall Syscall { get; }
         public SyscallHandler SyscallHandler { get; }
 
-        public CountdownEvent ThreadCounter { get; }
-
         public KResourceLimit ResourceLimit { get; }
 
         public KMemoryRegionManager[] MemoryRegions { get; }
@@ -57,8 +55,6 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             SyscallHandler = new SyscallHandler(this);
 
-            ThreadCounter = new CountdownEvent(1);
-
             ResourceLimit = new KResourceLimit(this);
 
             KernelInit.InitializeResourceLimit(ResourceLimit);

+ 17 - 17
Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs

@@ -791,19 +791,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
         private void InterruptHandler(object sender, EventArgs e)
         {
             KernelContext.Scheduler.ContextSwitch();
+            KernelContext.Scheduler.GetCurrentThread().HandlePostSyscall();
         }
 
         public void IncrementThreadCount()
         {
             Interlocked.Increment(ref _threadCount);
-
-            KernelContext.ThreadCounter.AddCount();
         }
 
         public void DecrementThreadCountAndTerminateIfZero()
         {
-            KernelContext.ThreadCounter.Signal();
-
             if (Interlocked.Decrement(ref _threadCount) == 0)
             {
                 Terminate();
@@ -812,8 +809,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
         public void DecrementToZeroWhileTerminatingCurrent()
         {
-            KernelContext.ThreadCounter.Signal();
-
             while (Interlocked.Decrement(ref _threadCount) != 0)
             {
                 Destroy();
@@ -1000,24 +995,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 KernelContext.CriticalSection.Leave();
             }
 
-            KThread blockedThread = null;
-
-            lock (_threadingLock)
+            while (true)
             {
-                foreach (KThread thread in _threads)
+                KThread blockedThread = null;
+
+                lock (_threadingLock)
                 {
-                    if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
+                    foreach (KThread thread in _threads)
                     {
-                        thread.IncrementReferenceCount();
+                        if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
+                        {
+                            thread.IncrementReferenceCount();
 
-                        blockedThread = thread;
-                        break;
+                            blockedThread = thread;
+                            break;
+                        }
                     }
                 }
-            }
 
-            if (blockedThread != null)
-            {
+                if (blockedThread == null)
+                {
+                    break;
+                }
+
                 blockedThread.Terminate();
                 blockedThread.DecrementReferenceCount();
             }

+ 1 - 53
Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs

@@ -2,14 +2,12 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Cpu;
 using Ryujinx.HLE.Exceptions;
-using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using System;
-using System.Threading;
 
 namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 {
@@ -26,29 +24,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
         // IPC
 
-        private struct HleIpcMessage
-        {
-            public KProcess Process { get; }
-            public KThread Thread { get; }
-            public KClientSession Session { get; }
-            public IpcMessage Message { get; }
-            public long MessagePtr { get; }
-
-            public HleIpcMessage(
-                KProcess process,
-                KThread thread,
-                KClientSession session,
-                IpcMessage message,
-                long messagePtr)
-            {
-                Process = process;
-                Thread = thread;
-                Session = session;
-                Message = message;
-                MessagePtr = messagePtr;
-            }
-        }
-
         public KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
         {
             handle = 0;
@@ -135,16 +110,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
                 currentThread.Reschedule(ThreadSchedState.Paused);
 
-                IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
-
-                ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
-                    process,
-                    currentThread,
-                    clientSession,
-                    message,
-                    (long)messagePtr));
-
-                _context.ThreadCounter.AddCount();
+                clientSession.Service.Server.PushMessage(_device, currentThread, clientSession, messagePtr, messageSize);
 
                 _context.CriticalSection.Leave();
 
@@ -158,24 +124,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             }
         }
 
-        private void ProcessIpcRequest(object state)
-        {
-            HleIpcMessage ipcMessage = (HleIpcMessage)state;
-
-            ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
-                _device,
-                ipcMessage.Process,
-                ipcMessage.Process.CpuMemory,
-                ipcMessage.Thread,
-                ipcMessage.Session,
-                ipcMessage.Message,
-                ipcMessage.MessagePtr);
-
-            _context.ThreadCounter.Signal();
-
-            ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
-        }
-
         private KernelResult SendSyncRequest(int handle)
         {
             KProcess currentProcess = _context.Scheduler.GetCurrentProcess();

+ 1 - 0
Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs

@@ -348,6 +348,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                     if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Running)
                     {
                         // TODO: GIC distributor stuffs (sgir changes ect)
+                        Context.RequestInterrupt();
                     }
 
                     SignaledObj   = null;

+ 1 - 1
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>)

+ 1 - 1
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;
 

+ 19 - 4
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);

+ 1 - 1
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;
         }

+ 60 - 32
Ryujinx.HLE/HOS/Ipc/IpcHandler.cs → Ryujinx.HLE/HOS/Services/ServerBase.cs

@@ -1,4 +1,5 @@
-using Ryujinx.Cpu;
+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;
@@ -6,19 +7,54 @@ using Ryujinx.HLE.HOS.Kernel.Threading;
 using System;
 using System.IO;
 
-namespace Ryujinx.HLE.HOS.Ipc
+namespace Ryujinx.HLE.HOS.Services
 {
-    static class IpcHandler
+    class ServerBase
     {
-        public static KernelResult IpcCall(
-            Switch         device,
-            KProcess       process,
-            MemoryManager  memory,
-            KThread        thread,
-            KClientSession session,
-            IpcMessage     request,
-            long           cmdPtr)
+        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))
@@ -35,17 +71,17 @@ namespace Ryujinx.HLE.HOS.Ipc
                         BinaryWriter resWriter = new BinaryWriter(resMs);
 
                         ServiceCtx context = new ServiceCtx(
-                            device,
-                            process,
-                            memory,
-                            thread,
-                            session,
+                            message.Device,
+                            message.Process,
+                            message.Process.CpuMemory,
+                            message.Thread,
+                            message.Session,
                             request,
                             response,
                             reqReader,
                             resWriter);
 
-                        session.Service.CallMethod(context);
+                        message.Session.Service.CallMethod(context);
 
                         response.RawData = resMs.ToArray();
                     }
@@ -59,26 +95,19 @@ namespace Ryujinx.HLE.HOS.Ipc
                     switch (cmdId)
                     {
                         case 0:
-                        {
-                            request = FillResponse(response, 0, session.Service.ConvertToDomain());
-
+                            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 (process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
+                            if (message.Process.HandleTable.GenerateHandle(message.Session, out int handle) != KernelResult.Success)
                             {
                                 throw new InvalidOperationException("Out of handles!");
                             }
@@ -88,25 +117,24 @@ namespace Ryujinx.HLE.HOS.Ipc
                             request = FillResponse(response, 0);
 
                             break;
-                        }
 
                         default: throw new NotImplementedException(cmdId.ToString());
                     }
                 }
                 else if (request.Type == IpcMessageType.CloseSession)
                 {
-                    // TODO
-                    return KernelResult.PortRemoteClosed;
+                    message.SignalDone(KernelResult.PortRemoteClosed);
+                    return;
                 }
                 else
                 {
                     throw new NotImplementedException(request.Type.ToString());
                 }
 
-                memory.Write((ulong)cmdPtr, response.GetBytes(cmdPtr));
+                message.Process.CpuMemory.Write(message.MessagePtr, response.GetBytes((long)message.MessagePtr));
             }
 
-            return KernelResult.Success;
+            message.SignalDone(KernelResult.Success);
         }
 
         private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
@@ -146,4 +174,4 @@ namespace Ryujinx.HLE.HOS.Ipc
             return response;
         }
     }
-}
+}

+ 1 - 1
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;

+ 15 - 4
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
                 {

+ 1 - 1
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;
         }

+ 1 - 1
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>