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
This commit is contained in:
gdkchan 2021-08-04 15:28:33 -03:00 committed by GitHub
parent 06cd3abe6c
commit a27986c311
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 45 additions and 115 deletions

View file

@ -3,7 +3,7 @@ using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
@ -12,11 +12,10 @@ namespace Ryujinx.Audio.Backends.OpenAL
{ {
public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
{ {
private object _lock = new object(); private readonly ALDevice _device;
private ALDevice _device; private readonly ALContext _context;
private ALContext _context; private readonly ManualResetEvent _updateRequiredEvent;
private ManualResetEvent _updateRequiredEvent; private readonly ConcurrentDictionary<OpenALHardwareDeviceSession, byte> _sessions;
private List<OpenALHardwareDeviceSession> _sessions;
private bool _stillRunning; private bool _stillRunning;
private Thread _updaterThread; private Thread _updaterThread;
@ -25,7 +24,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
_device = ALC.OpenDevice(""); _device = ALC.OpenDevice("");
_context = ALC.CreateContext(_device, new ALContextAttributes()); _context = ALC.CreateContext(_device, new ALContextAttributes());
_updateRequiredEvent = new ManualResetEvent(false); _updateRequiredEvent = new ManualResetEvent(false);
_sessions = new List<OpenALHardwareDeviceSession>(); _sessions = new ConcurrentDictionary<OpenALHardwareDeviceSession, byte>();
_stillRunning = true; _stillRunning = true;
_updaterThread = new Thread(Update) _updaterThread = new Thread(Update)
@ -72,22 +71,16 @@ namespace Ryujinx.Audio.Backends.OpenAL
throw new ArgumentException($"{channelCount}"); 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) return _sessions.TryRemove(session, out _);
{
_sessions.Remove(session);
}
} }
public ManualResetEvent GetUpdateRequiredEvent() public ManualResetEvent GetUpdateRequiredEvent()
@ -103,16 +96,13 @@ namespace Ryujinx.Audio.Backends.OpenAL
{ {
bool updateRequired = false; 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;
} }
} }
}
if (updateRequired) if (updateRequired)
{ {
@ -135,26 +125,10 @@ namespace Ryujinx.Audio.Backends.OpenAL
{ {
_stillRunning = false; _stillRunning = false;
int sessionCount = 0; foreach (OpenALHardwareDeviceSession session in _sessions.Keys)
// 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)
{
if (_sessions.Count == 0)
{
break;
}
OpenALHardwareDeviceSession session = _sessions[_sessions.Count - 1];
session.Dispose(); session.Dispose();
sessionCount = _sessions.Count;
} }
}
while (sessionCount > 0);
ALC.DestroyContext(_context); ALC.DestroyContext(_context);
ALC.CloseDevice(_device); ALC.CloseDevice(_device);

View file

@ -190,7 +190,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing && _driver.Unregister(this))
{ {
lock (_lock) lock (_lock)
{ {
@ -198,8 +198,6 @@ namespace Ryujinx.Audio.Backends.OpenAL
Stop(); Stop();
AL.DeleteSource(_sourceId); AL.DeleteSource(_sourceId);
_driver.Unregister(this);
} }
} }
} }

View file

@ -1,10 +1,9 @@
using Ryujinx.Audio.Backends.Common; using Ryujinx.Audio.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Concurrent;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@ -15,15 +14,13 @@ namespace Ryujinx.Audio.Backends.SDL2
{ {
public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
{ {
private object _lock = new object(); private readonly ManualResetEvent _updateRequiredEvent;
private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions;
private ManualResetEvent _updateRequiredEvent;
private List<SDL2HardwareDeviceSession> _sessions;
public SDL2HardwareDeviceDriver() public SDL2HardwareDeviceDriver()
{ {
_updateRequiredEvent = new ManualResetEvent(false); _updateRequiredEvent = new ManualResetEvent(false);
_sessions = new List<SDL2HardwareDeviceSession>(); _sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>();
SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.Initialize();
} }
@ -64,22 +61,16 @@ namespace Ryujinx.Audio.Backends.SDL2
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!"); 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) return _sessions.TryRemove(session, out _);
{
_sessions.Remove(session);
}
} }
private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount) private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
@ -149,10 +140,8 @@ namespace Ryujinx.Audio.Backends.SDL2
{ {
if (disposing) if (disposing)
{ {
while (_sessions.Count > 0) foreach (SDL2HardwareDeviceSession session in _sessions.Keys)
{ {
SDL2HardwareDeviceSession session = _sessions[_sessions.Count - 1];
session.Dispose(); session.Dispose();
} }

View file

@ -201,7 +201,7 @@ namespace Ryujinx.Audio.Backends.SDL2
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing && _driver.Unregister(this))
{ {
PrepareToClose(); PrepareToClose();
Stop(); Stop();
@ -210,8 +210,6 @@ namespace Ryujinx.Audio.Backends.SDL2
{ {
SDL_CloseAudioDevice(_outputStream); SDL_CloseAudioDevice(_outputStream);
} }
_driver.Unregister(this);
} }
} }

View file

@ -3,7 +3,7 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Memory; using Ryujinx.Memory;
using SoundIOSharp; using SoundIOSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Concurrent;
using System.Threading; using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
@ -12,19 +12,17 @@ namespace Ryujinx.Audio.Backends.SoundIo
{ {
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
{ {
private object _lock = new object(); private readonly SoundIO _audioContext;
private readonly SoundIODevice _audioDevice;
private SoundIO _audioContext; private readonly ManualResetEvent _updateRequiredEvent;
private SoundIODevice _audioDevice; private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
private ManualResetEvent _updateRequiredEvent;
private List<SoundIoHardwareDeviceSession> _sessions;
private int _disposeState; private int _disposeState;
public SoundIoHardwareDeviceDriver() public SoundIoHardwareDeviceDriver()
{ {
_audioContext = new SoundIO(); _audioContext = new SoundIO();
_updateRequiredEvent = new ManualResetEvent(false); _updateRequiredEvent = new ManualResetEvent(false);
_sessions = new List<SoundIoHardwareDeviceSession>(); _sessions = new ConcurrentDictionary<SoundIoHardwareDeviceSession, byte>();
_audioContext.Connect(); _audioContext.Connect();
_audioContext.FlushEvents(); _audioContext.FlushEvents();
@ -142,22 +140,16 @@ namespace Ryujinx.Audio.Backends.SoundIo
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!"); 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) return _sessions.TryRemove(session, out _);
{
_sessions.Remove(session);
}
} }
public static SoundIOFormat GetSoundIoFormat(SampleFormat format) public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
@ -219,26 +211,10 @@ namespace Ryujinx.Audio.Backends.SoundIo
{ {
if (disposing) if (disposing)
{ {
int sessionCount = 0; foreach (SoundIoHardwareDeviceSession session in _sessions.Keys)
// 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(); session.Dispose();
sessionCount = _sessions.Count;
} }
}
while (sessionCount > 0);
_audioContext.Disconnect(); _audioContext.Disconnect();
_audioContext.Dispose(); _audioContext.Dispose();

View file

@ -423,14 +423,12 @@ namespace Ryujinx.Audio.Backends.SoundIo
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing && _driver.Unregister(this))
{ {
PrepareToClose(); PrepareToClose();
Stop(); Stop();
_outputStream.Dispose(); _outputStream.Dispose();
_driver.Unregister(this);
} }
} }

View file

@ -7,7 +7,6 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Configuration; using Ryujinx.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.GTK3; using Ryujinx.Input.GTK3;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
@ -138,8 +137,6 @@ namespace Ryujinx.Ui
{ {
ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged; ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
Window.Cursor = null;
NpadManager.Dispose(); NpadManager.Dispose();
Dispose(); Dispose();
} }