fix: complete retail parity stability pass
All checks were successful
CI / linux-portable (push) Successful in 3m41s
CI / windows-gate (push) Successful in 6m49s
CI / release (push) Successful in 3m22s

This commit is contained in:
Erik 2026-08-28 20:01:39 +02:00
parent d3df4cb20a
commit f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions

View file

@ -142,11 +142,6 @@ public sealed class DatSoundCacheTests
[Trait("Status", "KnownFailure")]
public void GetWave_ConcurrentSameId_PublishesOneCanonicalWaveAndDecodesOnce()
{
// #321: a stale caller can pass the resident-cache check, pause, and
// reach _inflight after the winning caller has decoded, admitted, and
// removed its Lazy. That second Lazy performs a duplicate decode.
// R3 preserves this product defect as an explicit known-failure lane;
// fixing the cache algorithm is outside the test-only cleanup scope.
var dats = new FakeDatObjectSource();
dats.AddWave(1, MakePcmWave(1, dataBytes: 1000));
var cache = new DatSoundCache(dats, maxWaveBytes: 10_000);
@ -161,6 +156,55 @@ public sealed class DatSoundCacheTests
Assert.Equal(1, dats.GetCallCount(1));
}
[Fact]
public async Task GetWave_StaleMissCannotDecodeAgainAfterWinnerPublishes()
{
using var winnerDecodeEntered = new ManualResetEventSlim();
using var releaseWinnerDecode = new ManualResetEventSlim();
using var staleMissParked = new ManualResetEventSlim();
using var releaseStaleMiss = new ManualResetEventSlim();
var dats = new FakeDatObjectSource
{
BeforeWaveGet = _ =>
{
winnerDecodeEntered.Set();
if (!releaseWinnerDecode.Wait(TimeSpan.FromSeconds(5)))
throw new TimeoutException("winner decode was never released");
},
};
dats.AddWave(1, MakePcmWave(1, dataBytes: 1000));
int initialMissOrdinal = 0;
var cache = new DatSoundCache(
dats,
maxWaveBytes: 10_000,
afterInitialWaveMiss: _ =>
{
if (Interlocked.Increment(ref initialMissOrdinal) != 2)
return;
staleMissParked.Set();
if (!releaseStaleMiss.Wait(TimeSpan.FromSeconds(5)))
throw new TimeoutException("stale miss was never released");
});
Task<WaveData?> winner = Task.Run(() => cache.GetWave(1));
Assert.True(winnerDecodeEntered.Wait(TimeSpan.FromSeconds(5)));
Task<WaveData?> stale = Task.Run(() => cache.GetWave(1));
Assert.True(staleMissParked.Wait(TimeSpan.FromSeconds(5)));
releaseWinnerDecode.Set();
WaveData? canonical = await winner;
releaseStaleMiss.Set();
WaveData? recovered = await stale;
Assert.NotNull(canonical);
Assert.Same(canonical, recovered);
Assert.Equal(1, dats.GetCallCount(1));
}
[Fact]
public void GetWave_ConcurrentMissingId_NeverThrowsAndAllReturnNull()
{
@ -214,6 +258,8 @@ public sealed class DatSoundCacheTests
public void AddWave(uint id, Wave wave) => _waves[id] = wave;
public Action<uint>? BeforeWaveGet { get; init; }
public int GetCallCount(uint id)
{
lock (_gate)
@ -225,6 +271,7 @@ public sealed class DatSoundCacheTests
{
if (typeof(T) == typeof(Wave))
{
BeforeWaveGet?.Invoke(fileId);
lock (_gate)
{
_callCounts.TryGetValue(fileId, out int count);