From a27986c31167d8ce60efcee7e901da241f63ed08 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 4 Aug 2021 15:28:33 -0300 Subject: [PATCH] Make audio disposal thread safe on all 3 backends (#2527) * Make audio disposal thread safe on all 3 backends * Make OpenAL more consistent with the other backends * Remove Window.Cursor = null, and change dummy TValue to byte --- .../OpenALHardwareDeviceDriver.cs | 58 +++++-------------- .../OpenALHardwareDeviceSession.cs | 4 +- .../SDL2HardwareDeviceDriver.cs | 33 ++++------- .../SDL2HardwareDeviceSession.cs | 4 +- .../SoundIoHardwareDeviceDriver.cs | 50 +++++----------- .../SoundIoHardwareDeviceSession.cs | 4 +- Ryujinx/Ui/RendererWidgetBase.cs | 7 +-- 7 files changed, 45 insertions(+), 115 deletions(-) diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs index 721e96c623..60c364da9c 100644 --- a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs @@ -3,7 +3,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using Ryujinx.Memory; using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Linq; using System.Threading; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; @@ -12,11 +12,10 @@ namespace Ryujinx.Audio.Backends.OpenAL { public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver { - private object _lock = new object(); - private ALDevice _device; - private ALContext _context; - private ManualResetEvent _updateRequiredEvent; - private List _sessions; + private readonly ALDevice _device; + private readonly ALContext _context; + private readonly ManualResetEvent _updateRequiredEvent; + private readonly ConcurrentDictionary _sessions; private bool _stillRunning; private Thread _updaterThread; @@ -25,7 +24,7 @@ namespace Ryujinx.Audio.Backends.OpenAL _device = ALC.OpenDevice(""); _context = ALC.CreateContext(_device, new ALContextAttributes()); _updateRequiredEvent = new ManualResetEvent(false); - _sessions = new List(); + _sessions = new ConcurrentDictionary(); _stillRunning = true; _updaterThread = new Thread(Update) @@ -72,22 +71,16 @@ namespace Ryujinx.Audio.Backends.OpenAL throw new ArgumentException($"{channelCount}"); } - lock (_lock) - { - OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); + OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); - _sessions.Add(session); + _sessions.TryAdd(session, 0); - return session; - } + return session; } - internal void Unregister(OpenALHardwareDeviceSession session) + internal bool Unregister(OpenALHardwareDeviceSession session) { - lock (_lock) - { - _sessions.Remove(session); - } + return _sessions.TryRemove(session, out _); } public ManualResetEvent GetUpdateRequiredEvent() @@ -103,14 +96,11 @@ namespace Ryujinx.Audio.Backends.OpenAL { bool updateRequired = false; - lock (_lock) + foreach (OpenALHardwareDeviceSession session in _sessions.Keys) { - foreach (OpenALHardwareDeviceSession session in _sessions) + if (session.Update()) { - if (session.Update()) - { - updateRequired = true; - } + updateRequired = true; } } @@ -135,26 +125,10 @@ namespace Ryujinx.Audio.Backends.OpenAL { _stillRunning = false; - 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 + foreach (OpenALHardwareDeviceSession session in _sessions.Keys) { - lock (_lock) - { - if (_sessions.Count == 0) - { - break; - } - - OpenALHardwareDeviceSession session = _sessions[_sessions.Count - 1]; - - session.Dispose(); - - sessionCount = _sessions.Count; - } + session.Dispose(); } - while (sessionCount > 0); ALC.DestroyContext(_context); ALC.CloseDevice(_device); diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs index f0227bf8de..f0c0f4980d 100644 --- a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs +++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -190,7 +190,7 @@ namespace Ryujinx.Audio.Backends.OpenAL protected virtual void Dispose(bool disposing) { - if (disposing) + if (disposing && _driver.Unregister(this)) { lock (_lock) { @@ -198,8 +198,6 @@ namespace Ryujinx.Audio.Backends.OpenAL Stop(); AL.DeleteSource(_sourceId); - - _driver.Unregister(this); } } } diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index 07131d1de1..2c1baa4725 100644 --- a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -1,10 +1,9 @@ -using Ryujinx.Audio.Backends.Common; -using Ryujinx.Audio.Common; +using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using Ryujinx.Memory; using Ryujinx.SDL2.Common; using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Runtime.InteropServices; using System.Threading; @@ -15,15 +14,13 @@ namespace Ryujinx.Audio.Backends.SDL2 { public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver { - private object _lock = new object(); - - private ManualResetEvent _updateRequiredEvent; - private List _sessions; + private readonly ManualResetEvent _updateRequiredEvent; + private readonly ConcurrentDictionary _sessions; public SDL2HardwareDeviceDriver() { _updateRequiredEvent = new ManualResetEvent(false); - _sessions = new List(); + _sessions = new ConcurrentDictionary(); SDL2Driver.Instance.Initialize(); } @@ -64,22 +61,16 @@ namespace Ryujinx.Audio.Backends.SDL2 throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!"); } - lock (_lock) - { - SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); + SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); - _sessions.Add(session); + _sessions.TryAdd(session, 0); - return session; - } + return session; } - internal void Unregister(SDL2HardwareDeviceSession session) + internal bool Unregister(SDL2HardwareDeviceSession session) { - lock (_lock) - { - _sessions.Remove(session); - } + return _sessions.TryRemove(session, out _); } private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount) @@ -149,10 +140,8 @@ namespace Ryujinx.Audio.Backends.SDL2 { if (disposing) { - while (_sessions.Count > 0) + foreach (SDL2HardwareDeviceSession session in _sessions.Keys) { - SDL2HardwareDeviceSession session = _sessions[_sessions.Count - 1]; - session.Dispose(); } diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs index 344dd9b616..ceb6e70634 100644 --- a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs +++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -201,7 +201,7 @@ namespace Ryujinx.Audio.Backends.SDL2 protected virtual void Dispose(bool disposing) { - if (disposing) + if (disposing && _driver.Unregister(this)) { PrepareToClose(); Stop(); @@ -210,8 +210,6 @@ namespace Ryujinx.Audio.Backends.SDL2 { SDL_CloseAudioDevice(_outputStream); } - - _driver.Unregister(this); } } diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs index b9b549e604..20aa4cbf65 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs @@ -3,7 +3,7 @@ using Ryujinx.Audio.Integration; using Ryujinx.Memory; using SoundIOSharp; using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Threading; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; @@ -12,19 +12,17 @@ namespace Ryujinx.Audio.Backends.SoundIo { public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver { - private object _lock = new object(); - - private SoundIO _audioContext; - private SoundIODevice _audioDevice; - private ManualResetEvent _updateRequiredEvent; - private List _sessions; + private readonly SoundIO _audioContext; + private readonly SoundIODevice _audioDevice; + private readonly ManualResetEvent _updateRequiredEvent; + private readonly ConcurrentDictionary _sessions; private int _disposeState; public SoundIoHardwareDeviceDriver() { _audioContext = new SoundIO(); _updateRequiredEvent = new ManualResetEvent(false); - _sessions = new List(); + _sessions = new ConcurrentDictionary(); _audioContext.Connect(); _audioContext.FlushEvents(); @@ -142,22 +140,16 @@ namespace Ryujinx.Audio.Backends.SoundIo throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!"); } - lock (_lock) - { - SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); + SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); - _sessions.Add(session); + _sessions.TryAdd(session, 0); - return session; - } + return session; } - internal void Unregister(SoundIoHardwareDeviceSession session) + internal bool Unregister(SoundIoHardwareDeviceSession session) { - lock (_lock) - { - _sessions.Remove(session); - } + return _sessions.TryRemove(session, out _); } public static SoundIOFormat GetSoundIoFormat(SampleFormat format) @@ -219,26 +211,10 @@ namespace Ryujinx.Audio.Backends.SoundIo { if (disposing) { - int sessionCount = 0; - - // 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 + foreach (SoundIoHardwareDeviceSession session in _sessions.Keys) { - lock (_lock) - { - if (_sessions.Count == 0) - { - break; - } - - SoundIoHardwareDeviceSession session = _sessions[_sessions.Count - 1]; - - session.Dispose(); - - sessionCount = _sessions.Count; - } + session.Dispose(); } - while (sessionCount > 0); _audioContext.Disconnect(); _audioContext.Dispose(); diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs index 884e75edda..ee2eeb777b 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs @@ -423,14 +423,12 @@ namespace Ryujinx.Audio.Backends.SoundIo protected virtual void Dispose(bool disposing) { - if (disposing) + if (disposing && _driver.Unregister(this)) { PrepareToClose(); Stop(); _outputStream.Dispose(); - - _driver.Unregister(this); } } diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index d74cfec10c..e1278b397b 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -7,7 +7,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Configuration; using Ryujinx.Graphics.GAL; -using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.Input; using Ryujinx.Input.GTK3; using Ryujinx.Input.HLE; @@ -138,8 +137,6 @@ namespace Ryujinx.Ui { ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged; - Window.Cursor = null; - NpadManager.Dispose(); Dispose(); } @@ -151,7 +148,7 @@ namespace Ryujinx.Ui _lastCursorMoveTime = Stopwatch.GetTimestamp(); } - if(ConfigurationState.Instance.Hid.EnableMouse) + if (ConfigurationState.Instance.Hid.EnableMouse) { Window.Cursor = _invisibleCursor; } @@ -609,7 +606,7 @@ namespace Ryujinx.Ui { state |= KeyboardHotkeyState.ToggleVSync; } - + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) { state |= KeyboardHotkeyState.Screenshot;