From d02eeed9c1ba8a8e4d18f18c24588f2953ad7ed6 Mon Sep 17 00:00:00 2001 From: Mary Date: Sun, 28 Feb 2021 17:20:34 +0100 Subject: [PATCH] Haydn: Make SoundIO session implementation lock-free (#2068) * Haydn: Fix race condition in SoundIO Update implementation This should fix weird crashes happening for some people with SoundIO. Fix #2062 * haydn: Make SoundIO session lock-free --- .../SoundIoAudioBuffer.cs | 11 +++- .../SoundIoHardwareDeviceSession.cs | 57 +++++++------------ 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs index b43143d6..7f32b953 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs @@ -2,8 +2,15 @@ { class SoundIoAudioBuffer { - public ulong DriverIdentifier; - public ulong SampleCount; + public readonly ulong DriverIdentifier; + public readonly ulong SampleCount; public ulong SamplePlayed; + + public SoundIoAudioBuffer(ulong driverIdentifier, ulong sampleCount) + { + DriverIdentifier = driverIdentifier; + SampleCount = sampleCount; + SamplePlayed = 0; + } } } diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs index be01ecdc..925a1cb4 100644 --- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs +++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs @@ -3,7 +3,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Memory; using SoundIOSharp; using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Runtime.CompilerServices; using System.Threading; @@ -11,10 +11,8 @@ namespace Ryujinx.Audio.Backends.SoundIo { class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase { - private object _lock = new object(); - private SoundIoHardwareDeviceDriver _driver; - private Queue _queuedBuffers; + private ConcurrentQueue _queuedBuffers; private SoundIOOutStream _outputStream; private DynamicRingBuffer _ringBuffer; private ulong _playedSampleCount; @@ -24,7 +22,7 @@ namespace Ryujinx.Audio.Backends.SoundIo { _driver = driver; _updateRequiredEvent = _driver.GetUpdateRequiredEvent(); - _queuedBuffers = new Queue(); + _queuedBuffers = new ConcurrentQueue(); _ringBuffer = new DynamicRingBuffer(); SetupOutputStream(); @@ -42,10 +40,7 @@ namespace Ryujinx.Audio.Backends.SoundIo public override ulong GetPlayedSampleCount() { - lock (_lock) - { - return _playedSampleCount; - } + return Interlocked.Read(ref _playedSampleCount); } public override float GetVolume() @@ -57,19 +52,11 @@ namespace Ryujinx.Audio.Backends.SoundIo public override void QueueBuffer(AudioBuffer buffer) { - lock (_lock) - { - SoundIoAudioBuffer driverBuffer = new SoundIoAudioBuffer - { - DriverIdentifier = buffer.DataPointer, - SampleCount = GetSampleCount(buffer), - SamplePlayed = 0, - }; + SoundIoAudioBuffer driverBuffer = new SoundIoAudioBuffer(buffer.DataPointer, GetSampleCount(buffer)); - _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); + _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); - _queuedBuffers.Enqueue(driverBuffer); - } + _queuedBuffers.Enqueue(driverBuffer); } public override void SetVolume(float volume) @@ -96,15 +83,12 @@ namespace Ryujinx.Audio.Backends.SoundIo public override bool WasBufferFullyConsumed(AudioBuffer buffer) { - lock (_lock) + if (!_queuedBuffers.TryPeek(out SoundIoAudioBuffer driverBuffer)) { - if (!_queuedBuffers.TryPeek(out SoundIoAudioBuffer driverBuffer)) - { - return true; - } - - return driverBuffer.DriverIdentifier != buffer.DataPointer; + return true; } + + return driverBuffer.DriverIdentifier != buffer.DataPointer; } private unsafe void Update(int minFrameCount, int maxFrameCount) @@ -413,20 +397,20 @@ namespace Ryujinx.Audio.Backends.SoundIo while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SoundIoAudioBuffer driverBuffer)) { - ulong sampleStillNeeded = driverBuffer.SampleCount - driverBuffer.SamplePlayed; + ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed); ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount); - driverBuffer.SamplePlayed += playedAudioBufferSampleCount; + Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount); availaibleSampleCount -= playedAudioBufferSampleCount; - if (driverBuffer.SamplePlayed == driverBuffer.SampleCount) + if (Interlocked.Read(ref driverBuffer.SamplePlayed) == driverBuffer.SampleCount) { _queuedBuffers.TryDequeue(out _); needUpdate = true; } - _playedSampleCount += playedAudioBufferSampleCount; + Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount); } // Notify the output if needed. @@ -440,15 +424,12 @@ namespace Ryujinx.Audio.Backends.SoundIo { if (disposing) { - lock (_lock) - { - PrepareToClose(); - Stop(); + PrepareToClose(); + Stop(); - _outputStream.Dispose(); + _outputStream.Dispose(); - _driver.Unregister(this); - } + _driver.Unregister(this); } }