From 00ce9eea620652b97b4d3e8cd9218c6fccff8b1c Mon Sep 17 00:00:00 2001 From: Mary Date: Tue, 29 Jun 2021 19:37:13 +0200 Subject: [PATCH] Fix disposing of IPC sessions server at emulation stop (#2334) --- .../OpenALHardwareDeviceDriver.cs | 23 +++++++---- .../SoundIoHardwareDeviceDriver.cs | 28 ++++++++++--- .../SoundIoHardwareDeviceSession.cs | 6 ++- Ryujinx.Audio/Input/AudioInputManager.cs | 25 ++++++++++- Ryujinx.Audio/Input/AudioInputSystem.cs | 13 +++++- Ryujinx.Audio/Output/AudioOutputManager.cs | 25 ++++++++++- Ryujinx.Audio/Output/AudioOutputSystem.cs | 11 ++++- .../Renderer/Server/AudioRenderSystem.cs | 7 +++- .../Renderer/Server/AudioRendererManager.cs | 10 ++++- Ryujinx.HLE/HOS/Horizon.cs | 9 ++-- .../ILibraryAppletAccessor.cs | 27 ++++++------ .../Services/Audio/AudioIn/AudioInServer.cs | 11 ++--- .../Services/Audio/AudioOut/AudioOutServer.cs | 11 ++--- .../AudioRenderer/AudioRendererServer.cs | 11 ++--- .../IDeliveryCacheDirectoryService.cs | 10 +++-- .../IDeliveryCacheFileService.cs | 10 +++-- .../IDeliveryCacheStorageService.cs | 10 +++-- .../HOS/Services/DisposableIpcService.cs | 20 +++++++++ .../ServiceCreator/INotificationService.cs | 9 ++-- .../HOS/Services/Fs/FileSystemProxy/IFile.cs | 12 ++---- .../Services/Fs/FileSystemProxy/IStorage.cs | 12 ++---- .../HOS/Services/Fs/ISaveDataInfoReader.cs | 9 ++-- Ryujinx.HLE/HOS/Services/IpcService.cs | 13 ++++++ .../Nifm/StaticService/IGeneralService.cs | 9 ++-- Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs | 15 ++++--- Ryujinx.HLE/HOS/Services/ServerBase.cs | 41 +++++++++++++++---- Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 7 ++++ .../HOS/Services/Spl/IRandomInterface.cs | 13 +++--- 28 files changed, 284 insertions(+), 123 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/DisposableIpcService.cs diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs index 387ae77242..721e96c623 100644 --- a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs @@ -133,19 +133,28 @@ namespace Ryujinx.Audio.Backends.OpenAL { if (disposing) { - lock (_lock) - { - _stillRunning = false; - _updaterThread.Join(); + _stillRunning = false; - // Loop against all sessions to dispose them (they will unregister themself) - while (_sessions.Count > 0) + int sessionCount = 0; + + // NOTE: This is done in a way to avoid possible situations when the OpenALHardwareDeviceSession is already being dispose in another thread but doesn't hold the lock and tries to Unregister. + do + { + lock (_lock) { - OpenALHardwareDeviceSession session = _sessions[0]; + if (_sessions.Count == 0) + { + break; + } + + OpenALHardwareDeviceSession session = _sessions[_sessions.Count - 1]; session.Dispose(); + + sessionCount = _sessions.Count; } } + while (sessionCount > 0); ALC.DestroyContext(_context); ALC.CloseDevice(_device); diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs index 00977fcbe3..b9b549e604 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Audio.Backends.SoundIo private SoundIODevice _audioDevice; private ManualResetEvent _updateRequiredEvent; private List _sessions; + private int _disposeState; public SoundIoHardwareDeviceDriver() { @@ -208,19 +209,36 @@ namespace Ryujinx.Audio.Backends.SoundIo public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) { if (disposing) { - while (_sessions.Count > 0) - { - SoundIoHardwareDeviceSession session = _sessions[_sessions.Count - 1]; + int sessionCount = 0; - session.Dispose(); + // NOTE: This is done in a way to avoid possible situations when the SoundIoHardwareDeviceSession is already being dispose in another thread but doesn't hold the lock and tries to Unregister. + do + { + lock (_lock) + { + if (_sessions.Count == 0) + { + break; + } + + SoundIoHardwareDeviceSession session = _sessions[_sessions.Count - 1]; + + session.Dispose(); + + sessionCount = _sessions.Count; + } } + while (sessionCount > 0); _audioContext.Disconnect(); _audioContext.Dispose(); diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs index 925a1cb474..884e75edda 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs @@ -17,6 +17,7 @@ namespace Ryujinx.Audio.Backends.SoundIo private DynamicRingBuffer _ringBuffer; private ulong _playedSampleCount; private ManualResetEvent _updateRequiredEvent; + private int _disposeState; public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { @@ -435,7 +436,10 @@ namespace Ryujinx.Audio.Backends.SoundIo public override void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } } } diff --git a/Ryujinx.Audio/Input/AudioInputManager.cs b/Ryujinx.Audio/Input/AudioInputManager.cs index e098ae9eda..5c1f01db36 100644 --- a/Ryujinx.Audio/Input/AudioInputManager.cs +++ b/Ryujinx.Audio/Input/AudioInputManager.cs @@ -21,6 +21,8 @@ using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Diagnostics; +using System.Linq; +using System.Threading; namespace Ryujinx.Audio.Input { @@ -61,6 +63,11 @@ namespace Ryujinx.Audio.Input /// private int _activeSessionCount; + /// + /// The dispose state. + /// + private int _disposeState; + /// /// Create a new . /// @@ -248,14 +255,28 @@ namespace Ryujinx.Audio.Input public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) { if (disposing) { - // Nothing to do here. + // Clone the sessions array to dispose them outside the lock. + AudioInputSystem[] sessions; + + lock (_sessionLock) + { + sessions = _sessions.ToArray(); + } + + foreach (AudioInputSystem input in sessions) + { + input?.Dispose(); + } } } } diff --git a/Ryujinx.Audio/Input/AudioInputSystem.cs b/Ryujinx.Audio/Input/AudioInputSystem.cs index 8064a9470c..b3fd91e73f 100644 --- a/Ryujinx.Audio/Input/AudioInputSystem.cs +++ b/Ryujinx.Audio/Input/AudioInputSystem.cs @@ -18,6 +18,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using System; +using System.Threading; namespace Ryujinx.Audio.Input { @@ -62,10 +63,15 @@ namespace Ryujinx.Audio.Input private AudioInputManager _manager; /// - /// THe lock of the parent. + /// The lock of the parent. /// private object _parentLock; + /// + /// The dispose state. + /// + private int _disposeState; + /// /// Create a new . /// @@ -384,7 +390,10 @@ namespace Ryujinx.Audio.Input public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) diff --git a/Ryujinx.Audio/Output/AudioOutputManager.cs b/Ryujinx.Audio/Output/AudioOutputManager.cs index baa8499714..852632faef 100644 --- a/Ryujinx.Audio/Output/AudioOutputManager.cs +++ b/Ryujinx.Audio/Output/AudioOutputManager.cs @@ -21,6 +21,8 @@ using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Diagnostics; +using System.Linq; +using System.Threading; namespace Ryujinx.Audio.Output { @@ -61,6 +63,11 @@ namespace Ryujinx.Audio.Output /// private int _activeSessionCount; + /// + /// The dispose state. + /// + private int _disposeState; + /// /// Create a new . /// @@ -242,14 +249,28 @@ namespace Ryujinx.Audio.Output public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) { if (disposing) { - // Nothing to do here. + // Clone the sessions array to dispose them outside the lock. + AudioOutputSystem[] sessions; + + lock (_sessionLock) + { + sessions = _sessions.ToArray(); + } + + foreach (AudioOutputSystem output in sessions) + { + output?.Dispose(); + } } } } diff --git a/Ryujinx.Audio/Output/AudioOutputSystem.cs b/Ryujinx.Audio/Output/AudioOutputSystem.cs index f5db9d7a7f..d32d417a60 100644 --- a/Ryujinx.Audio/Output/AudioOutputSystem.cs +++ b/Ryujinx.Audio/Output/AudioOutputSystem.cs @@ -18,6 +18,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using System; +using System.Threading; namespace Ryujinx.Audio.Output { @@ -66,6 +67,11 @@ namespace Ryujinx.Audio.Output /// private object _parentLock; + /// + /// The dispose state. + /// + private int _disposeState; + /// /// Create a new . /// @@ -357,7 +363,10 @@ namespace Ryujinx.Audio.Output public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) diff --git a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 943a2d7804..6aed3c5d59 100644 --- a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -94,6 +94,8 @@ namespace Ryujinx.Audio.Renderer.Server private AudioRendererManager _manager; + private int _disposeState; + public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) { _manager = manager; @@ -811,7 +813,10 @@ namespace Ryujinx.Audio.Renderer.Server public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) diff --git a/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs index 004ac656b3..71d0f3182c 100644 --- a/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs +++ b/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs @@ -82,6 +82,11 @@ namespace Ryujinx.Audio.Renderer.Server /// public AudioProcessor Processor { get; } + /// + /// The dispose state. + /// + private int _disposeState; + /// /// Create a new . /// @@ -313,7 +318,10 @@ namespace Ryujinx.Audio.Renderer.Server public void Dispose() { - Dispose(true); + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } } protected virtual void Dispose(bool disposing) diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index a4ed7c377b..916ed7973c 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -70,6 +70,7 @@ namespace Ryujinx.HLE.HOS internal List NfpDevices { get; private set; } + internal ServerBase SmServer { get; private set; } internal ServerBase BsdServer { get; private set; } internal ServerBase AudRenServer { get; private set; } internal ServerBase AudOutServer { get; private set; } @@ -284,13 +285,11 @@ namespace Ryujinx.HLE.HOS public void InitializeServices() { - IUserInterface sm = new IUserInterface(KernelContext); - sm.TrySetServer(new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext))); + SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext)); // Wait until SM server thread is done with initialization, // only then doing connections to SM is safe. - sm.Server.InitDone.WaitOne(); - sm.Server.InitDone.Dispose(); + SmServer.InitDone.WaitOne(); BsdServer = new ServerBase(KernelContext, "BsdServer"); AudRenServer = new ServerBase(KernelContext, "AudioRendererServer"); @@ -419,7 +418,7 @@ namespace Ryujinx.HLE.HOS SurfaceFlinger.Dispose(); // Terminate HLE services (must be done after the application is already terminated, - // otherwise the application will receive errors due to service termination. + // otherwise the application will receive errors due to service termination). foreach (KProcess process in KernelContext.Processes.Values.Where(x => !x.Flags.HasFlag(ProcessCreationFlags.IsApplication))) { process.Terminate(); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 1377eac09f..2deb830eca 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -8,7 +8,7 @@ using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator { - class ILibraryAppletAccessor : IpcService, IDisposable + class ILibraryAppletAccessor : DisposableIpcService { private KernelContext _kernelContext; @@ -241,21 +241,24 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - if (_stateChangedEventHandle != 0) + if (isDisposing) { - _kernelContext.Syscall.CloseHandle(_stateChangedEventHandle); - } + if (_stateChangedEventHandle != 0) + { + _kernelContext.Syscall.CloseHandle(_stateChangedEventHandle); + } - if (_normalOutDataEventHandle != 0) - { - _kernelContext.Syscall.CloseHandle(_normalOutDataEventHandle); - } + if (_normalOutDataEventHandle != 0) + { + _kernelContext.Syscall.CloseHandle(_normalOutDataEventHandle); + } - if (_interactiveOutDataEventHandle != 0) - { - _kernelContext.Syscall.CloseHandle(_interactiveOutDataEventHandle); + if (_interactiveOutDataEventHandle != 0) + { + _kernelContext.Syscall.CloseHandle(_interactiveOutDataEventHandle); + } } } } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs index b45a4d2cbb..f9a9447f47 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn { - class AudioInServer : IpcService, IDisposable + class AudioInServer : DisposableIpcService { private IAudioIn _impl; @@ -193,14 +193,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn return ResultCode.Success; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) + if (isDisposing) { _impl.Dispose(); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs index b7515e0fa3..aff08811c0 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut { - class AudioOutServer : IpcService, IDisposable + class AudioOutServer : DisposableIpcService { private IAudioOut _impl; @@ -174,14 +174,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut return ResultCode.Success; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) + if (isDisposing) { _impl.Dispose(); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index bb51b50692..bd5030f3af 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -7,7 +7,7 @@ using System.Buffers; namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { - class AudioRendererServer : IpcService, IDisposable + class AudioRendererServer : DisposableIpcService { private IAudioRenderer _impl; @@ -172,14 +172,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer return result; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) + if (isDisposing) { _impl.Dispose(); } diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs index 51d8f66cc9..46c2c09c9f 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs @@ -1,12 +1,11 @@ using LibHac; using LibHac.Bcat; using Ryujinx.Common; -using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { - class IDeliveryCacheDirectoryService : IpcService, IDisposable + class IDeliveryCacheDirectoryService : DisposableIpcService { private LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService _base; @@ -55,9 +54,12 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - _base?.Dispose(); + if (isDisposing) + { + _base?.Dispose(); + } } } } diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs index 9354b60e36..55c89a3ebf 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs @@ -1,11 +1,10 @@ using LibHac; using LibHac.Bcat; using Ryujinx.Common; -using System; namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { - class IDeliveryCacheFileService : IpcService, IDisposable + class IDeliveryCacheFileService : DisposableIpcService { private LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService _base; @@ -68,9 +67,12 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - _base?.Dispose(); + if (isDisposing) + { + _base?.Dispose(); + } } } } diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs index cac5f1704e..0d2f25213b 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs @@ -1,11 +1,10 @@ using LibHac; using LibHac.Bcat; -using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator { - class IDeliveryCacheStorageService : IpcService, IDisposable + class IDeliveryCacheStorageService : DisposableIpcService { private LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService _base; @@ -60,9 +59,12 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return (ResultCode)result.Value; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - _base?.Dispose(); + if (isDisposing) + { + _base?.Dispose(); + } } } } diff --git a/Ryujinx.HLE/HOS/Services/DisposableIpcService.cs b/Ryujinx.HLE/HOS/Services/DisposableIpcService.cs new file mode 100644 index 0000000000..7aecdfd118 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/DisposableIpcService.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services +{ + abstract class DisposableIpcService : IpcService, IDisposable + { + private int _disposeState; + + protected abstract void Dispose(bool isDisposing); + + public void Dispose() + { + if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) + { + Dispose(true); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs index 700d4ab4fc..f5614dddcf 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs @@ -9,7 +9,7 @@ using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { - class INotificationService : IpcService, IDisposable + class INotificationService : DisposableIpcService { private readonly UserId _userId; private readonly FriendServicePermissionLevel _permissionLevel; @@ -167,9 +167,12 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator } } - public void Dispose() + protected override void Dispose(bool isDisposing) { - NotificationEventHandler.Instance.UnregisterNotificationService(this); + if (isDisposing) + { + NotificationEventHandler.Instance.UnregisterNotificationService(this); + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs index 681b6c17df..cf1611e789 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs @@ -1,10 +1,9 @@ using LibHac; using LibHac.Fs; -using System; namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy { - class IFile : IpcService, IDisposable + class IFile : DisposableIpcService { private LibHac.Fs.Fsa.IFile _baseFile; @@ -82,14 +81,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) + if (isDisposing) { _baseFile?.Dispose(); } diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs index 8995563471..62a3c62ad3 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -1,10 +1,9 @@ using LibHac; using Ryujinx.HLE.HOS.Ipc; -using System; namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy { - class IStorage : IpcService, IDisposable + class IStorage : DisposableIpcService { private LibHac.Fs.IStorage _baseStorage; @@ -53,14 +52,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy return (ResultCode)result.Value; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) + if (isDisposing) { _baseStorage?.Dispose(); } diff --git a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs index d6449a2da8..bc4a2eb95d 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs @@ -3,7 +3,7 @@ using LibHac; namespace Ryujinx.HLE.HOS.Services.Fs { - class ISaveDataInfoReader : IpcService, IDisposable + class ISaveDataInfoReader : DisposableIpcService { private ReferenceCountedDisposable _baseReader; @@ -29,9 +29,12 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)result.Value; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - _baseReader.Dispose(); + if (isDisposing) + { + _baseReader?.Dispose(); + } } } } diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index e9582c2631..e33060712b 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -265,5 +265,18 @@ namespace Ryujinx.HLE.HOS.Services { _parent = parent._parent; } + + public virtual void DestroyAtExit() + { + foreach (object domainObject in _domainObjects.Values) + { + if (domainObject != this && domainObject is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + } + + _domainObjects.Clear(); + } } } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index e650879b81..2296838e74 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -10,7 +10,7 @@ using System.Text; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { - class IGeneralService : IpcService, IDisposable + class IGeneralService : DisposableIpcService { private GeneralServiceDetail _generalServiceDetail; @@ -197,9 +197,12 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return (targetProperties, targetAddressInfo); } - public void Dispose() + protected override void Dispose(bool isDisposing) { - GeneralServiceManager.Remove(_generalServiceDetail.ClientId); + if (isDisposing) + { + GeneralServiceManager.Remove(_generalServiceDetail.ClientId); + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index ff5db94f7b..aa17f6acf2 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro { [Service("ldr:ro")] [Service("ro:1")] // 7.0.0+ - class IRoInterface : IpcService, IDisposable + class IRoInterface : DisposableIpcService { private const int MaxNrr = 0x40; private const int MaxNro = 0x40; @@ -571,14 +571,17 @@ namespace Ryujinx.HLE.HOS.Services.Ro return ResultCode.Success; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - foreach (NroInfo info in _nroInfos) + if (isDisposing) { - UnmapNroFromInfo(info); - } + foreach (NroInfo info in _nroInfos) + { + UnmapNroFromInfo(info); + } - _nroInfos.Clear(); + _nroInfos.Clear(); + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index c9d009a9a5..695394a59f 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -4,6 +4,7 @@ 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 Ryujinx.HLE.HOS.Services.Sm; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -12,7 +13,7 @@ using System.Threading; namespace Ryujinx.HLE.HOS.Services { - class ServerBase + class ServerBase : IDisposable { // Must be the maximum value used by services (highest one know is the one used by nvservices = 0x8000). // Having a size that is too low will cause failures as data copy will fail if the receiving buffer is @@ -67,6 +68,9 @@ namespace Ryujinx.HLE.HOS.Services public void AddSessionObj(KServerSession serverSession, IpcService obj) { + // Ensure that the sever loop is running. + InitDone.WaitOne(); + _selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle); AddSessionObj(serverSessionHandle, obj); } @@ -86,13 +90,9 @@ namespace Ryujinx.HLE.HOS.Services _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle); AddPort(serverPortHandle, SmObjectFactory); + } - InitDone.Set(); - } - else - { - InitDone.Dispose(); - } + InitDone.Set(); KThread thread = KernelStatic.GetCurrentThread(); ulong messagePtr = thread.TlsAddress; @@ -153,6 +153,8 @@ namespace Ryujinx.HLE.HOS.Services _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); } } + + Dispose(); } private bool Process(int serverSessionHandle, ulong recvListAddr) @@ -349,5 +351,30 @@ namespace Ryujinx.HLE.HOS.Services return response; } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + foreach (IpcService service in _sessions.Values) + { + if (service is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + + service.DestroyAtExit(); + } + + _sessions.Clear(); + + InitDone.Dispose(); + } + } + + public void Dispose() + { + Dispose(true); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 8b1ec5b81b..a5595e3100 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -245,5 +245,12 @@ namespace Ryujinx.HLE.HOS.Services.Sm return name; } + + public override void DestroyAtExit() + { + _commonServer.Dispose(); + + base.DestroyAtExit(); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs index 62e118d942..0f38e685bf 100644 --- a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs @@ -4,10 +4,12 @@ using System.Security.Cryptography; namespace Ryujinx.HLE.HOS.Services.Spl { [Service("csrng")] - class IRandomInterface : IpcService, IDisposable + class IRandomInterface : DisposableIpcService { private RNGCryptoServiceProvider _rng; + private object _lock = new object(); + public IRandomInterface(ServiceCtx context) { _rng = new RNGCryptoServiceProvider(); @@ -26,14 +28,9 @@ namespace Ryujinx.HLE.HOS.Services.Spl return ResultCode.Success; } - public void Dispose() + protected override void Dispose(bool isDisposing) { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) + if (isDisposing) { _rng.Dispose(); }