From 70895bdb04c51e92b4d84946c66c122f9d54a73f Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 11 Jun 2022 14:58:30 -0300
Subject: [PATCH] Allow concurrent BSD EventFd read/write (#3385)

---
 Ryujinx.HLE/HOS/Horizon.cs                    |  2 +-
 Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs    |  5 ++-
 .../HOS/Kernel/SupervisorCall/Syscall.cs      | 15 +++++++-
 Ryujinx.HLE/HOS/Services/ServerBase.cs        | 38 +++++++++++++++++--
 .../Sockets/Bsd/Impl/EventFileDescriptor.cs   |  9 ++---
 5 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index b93ebc0329..2e64542e5f 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -309,7 +309,7 @@ namespace Ryujinx.HLE.HOS
             // only then doing connections to SM is safe.
             SmServer.InitDone.WaitOne();
 
-            BsdServer = new ServerBase(KernelContext, "BsdServer");
+            BsdServer = new ServerBase(KernelContext, "BsdServer", null, 2);
             AudRenServer = new ServerBase(KernelContext, "AudioRendererServer");
             AudOutServer = new ServerBase(KernelContext, "AudioOutServer");
             FsServer = new ServerBase(KernelContext, "FsServer");
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index 0caeacade0..d01c3e3b24 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -735,11 +735,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             ulong argsPtr,
             ulong stackTop,
             int priority,
-            int cpuCore)
+            int cpuCore,
+            ThreadStart customThreadStart = null)
         {
             lock (_processLock)
             {
-                return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null);
+                return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, customThreadStart);
             }
         }
 
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index 179c7d4b2e..189e4a3ead 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -2350,6 +2350,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             [PointerSized] ulong stackTop,
             int priority,
             int cpuCore)
+        {
+            return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null);
+        }
+
+        public KernelResult CreateThread(
+            out int handle,
+            ulong entrypoint,
+            ulong argsPtr,
+            ulong stackTop,
+            int priority,
+            int cpuCore,
+            ThreadStart customThreadStart)
         {
             handle = 0;
 
@@ -2386,7 +2398,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 argsPtr,
                 stackTop,
                 priority,
-                cpuCore);
+                cpuCore,
+                customThreadStart);
 
             if (result == KernelResult.Success)
             {
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index 9078334417..2aead42e3d 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Common;
@@ -38,15 +39,18 @@ namespace Ryujinx.HLE.HOS.Services
         private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
 
         public ManualResetEvent InitDone { get; }
-        public Func<IpcService> SmObjectFactory { get; }
         public string Name { get; }
+        public Func<IpcService> SmObjectFactory { get; }
 
-        public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
+        private int _threadCount;
+
+        public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null, int threadCount = 1)
         {
             InitDone = new ManualResetEvent(false);
+            _context = context;
             Name = name;
             SmObjectFactory = smObjectFactory;
-            _context = context;
+            _threadCount = threadCount;
 
             const ProcessCreationFlags flags =
                 ProcessCreationFlags.EnableAslr |
@@ -56,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services
 
             ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
 
-            KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
+            KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main);
         }
 
         private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
@@ -80,6 +84,32 @@ namespace Ryujinx.HLE.HOS.Services
             _sessions.Add(serverSessionHandle, obj);
         }
 
+        private void Main()
+        {
+            for (int i = 1; i < _threadCount; i++)
+            {
+                KernelResult result = _context.Syscall.CreateThread(out int threadHandle, 0UL, 0UL, 0UL, 44, 3, ServerLoop);
+
+                if (result == KernelResult.Success)
+                {
+                    result = _context.Syscall.StartThread(threadHandle);
+
+                    if (result != KernelResult.Success)
+                    {
+                        Logger.Error?.Print(LogClass.Service, $"Failed to start thread on {Name}: {result}");
+                    }
+
+                    _context.Syscall.CloseHandle(threadHandle);
+                }
+                else
+                {
+                    Logger.Error?.Print(LogClass.Service, $"Failed to create thread on {Name}: {result}");
+                }
+            }
+
+            ServerLoop();
+        }
+
         private void ServerLoop()
         {
             _selfProcess = KernelStatic.GetCurrentProcess();
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs
index e92b42ef0a..ea63e84254 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs
@@ -8,7 +8,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
     {
         private ulong _value;
         private readonly EventFdFlags _flags;
-        private AutoResetEvent _event;
 
         private object _lock = new object();
 
@@ -21,7 +20,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
         {
             _value = value;
             _flags = flags;
-            _event = new AutoResetEvent(false);
 
             WriteEvent = new ManualResetEvent(true);
             ReadEvent = new ManualResetEvent(true);
@@ -31,7 +29,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
 
         public void Dispose()
         {
-            _event.Dispose();
             WriteEvent.Dispose();
             ReadEvent.Dispose();
         }
@@ -57,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
                     {
                         while (_value == 0)
                         {
-                            _event.WaitOne();
+                            Monitor.Wait(_lock);
                         }
                     }
                     else
@@ -106,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
                 {
                     if (Blocking)
                     {
-                        _event.WaitOne();
+                        Monitor.Wait(_lock);
                     }
                     else
                     {
@@ -119,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
                 writeSize = sizeof(ulong);
 
                 _value += count;
-                _event.Set();
+                Monitor.Pulse(_lock);
 
                 WriteEvent.Set();