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
This commit is contained in:
Mary 2021-02-28 17:20:34 +01:00 committed by GitHub
parent 460a98390e
commit d02eeed9c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 40 deletions

View file

@ -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;
}
}
}

View file

@ -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<SoundIoAudioBuffer> _queuedBuffers;
private ConcurrentQueue<SoundIoAudioBuffer> _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<SoundIoAudioBuffer>();
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
_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);
}
}