diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index 9d7e4d4c5..145680594 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -39,6 +39,8 @@ namespace Ryujinx.HLE.HOS.Services private readonly KernelContext _context; private KProcess _selfProcess; private KThread _selfThread; + private KEvent _wakeEvent; + private int _wakeHandle = 0; private readonly ReaderWriterLockSlim _handleLock = new(); private readonly Dictionary _sessions = new(); @@ -125,6 +127,8 @@ namespace Ryujinx.HLE.HOS.Services _handleLock.ExitWriteLock(); } } + + _wakeEvent.WritableEvent.Signal(); } private IpcService GetSessionObj(int serverSessionHandle) @@ -195,9 +199,11 @@ namespace Ryujinx.HLE.HOS.Services _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10); _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); - int replyTargetHandle = 0; + _wakeEvent = new KEvent(_context); + Result result = _selfProcess.HandleTable.GenerateHandle(_wakeEvent.ReadableEvent, out _wakeHandle); + while (true) { int portHandleCount; @@ -211,13 +217,15 @@ namespace Ryujinx.HLE.HOS.Services portHandleCount = _ports.Count; - handleCount = portHandleCount + _sessions.Count; + handleCount = portHandleCount + _sessions.Count + 1; handles = ArrayPool.Shared.Rent(handleCount); - _ports.Keys.CopyTo(handles, 0); + handles[0] = _wakeHandle; - _sessions.Keys.CopyTo(handles, portHandleCount); + _ports.Keys.CopyTo(handles, 1); + + _sessions.Keys.CopyTo(handles, portHandleCount + 1); } finally { @@ -227,8 +235,7 @@ namespace Ryujinx.HLE.HOS.Services } } - // We still need a timeout here to allow the service to pick up and listen new sessions... - var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L); + var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, -1); _selfThread.HandlePostSyscall(); @@ -239,7 +246,7 @@ namespace Ryujinx.HLE.HOS.Services replyTargetHandle = 0; - if (rc == Result.Success && signaledIndex >= portHandleCount) + if (rc == Result.Success && signaledIndex >= portHandleCount + 1) { // We got a IPC request, process it, pass to the appropriate service if needed. int signaledHandle = handles[signaledIndex]; @@ -253,24 +260,32 @@ namespace Ryujinx.HLE.HOS.Services { if (rc == Result.Success) { - // We got a new connection, accept the session to allow servicing future requests. - if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success) + if (signaledIndex > 0) { - bool handleWriteLockTaken = false; - try + // We got a new connection, accept the session to allow servicing future requests. + if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success) { - handleWriteLockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); - IpcService obj = _ports[handles[signaledIndex]].Invoke(); - _sessions.Add(serverSessionHandle, obj); - } - finally - { - if (handleWriteLockTaken) + bool handleWriteLockTaken = false; + try { - _handleLock.ExitWriteLock(); + handleWriteLockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite); + IpcService obj = _ports[handles[signaledIndex]].Invoke(); + _sessions.Add(serverSessionHandle, obj); + } + finally + { + if (handleWriteLockTaken) + { + _handleLock.ExitWriteLock(); + } } } } + else + { + // The _wakeEvent signalled, which means we have a new session. + _wakeEvent.WritableEvent.Clear(); + } } _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); @@ -499,6 +514,8 @@ namespace Ryujinx.HLE.HOS.Services if (Interlocked.Exchange(ref _isDisposed, 1) == 0) { + _selfProcess.HandleTable.CloseHandle(_wakeHandle); + foreach (IpcService service in _sessions.Values) { (service as IDisposable)?.Dispose();