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

@ -81,6 +81,7 @@ public sealed class DatSoundCache
private readonly Dictionary<uint, LinkedListNode<WaveEntry>> _waveEntries = new();
private readonly LinkedList<WaveEntry> _waveLru = new();
private readonly long _maxWaveBytes;
private readonly Action<uint>? _afterInitialWaveMiss;
private long _residentWaveBytes;
private long _hits;
private long _misses;
@ -98,11 +99,24 @@ public sealed class DatSoundCache
/// exercised deterministically without allocating tens of megabytes.
/// </summary>
public DatSoundCache(IDatObjectSource dats, long maxWaveBytes)
: this(dats, maxWaveBytes, afterInitialWaveMiss: null)
{
}
/// <summary>
/// Deterministic concurrency seam used to park a caller after its fast
/// resident lookup but before the authoritative locked recheck.
/// </summary>
internal DatSoundCache(
IDatObjectSource dats,
long maxWaveBytes,
Action<uint>? afterInitialWaveMiss)
{
ArgumentNullException.ThrowIfNull(dats);
ArgumentOutOfRangeException.ThrowIfLessThan(maxWaveBytes, 1);
_dats = dats;
_maxWaveBytes = maxWaveBytes;
_afterInitialWaveMiss = afterInitialWaveMiss;
}
/// <summary>
@ -128,14 +142,37 @@ public sealed class DatSoundCache
return null;
}
Interlocked.Increment(ref _misses);
_afterInitialWaveMiss?.Invoke(waveId);
Lazy<WaveData?> lazy = _inflight.GetOrAdd(
waveId,
static (id, self) => new Lazy<WaveData?>(
() => self.DecodeUncached(id),
LazyThreadSafetyMode.ExecutionAndPublication),
this);
Lazy<WaveData?> lazy;
lock (_gate)
{
// A caller can pause after the fast miss while another caller
// decodes, admits, and removes the in-flight Lazy. Recheck the
// authoritative caches and acquire/create the Lazy under the same
// gate used by admission so that stale caller cannot start a
// second decode after publication.
if (_waveEntries.TryGetValue(waveId, out var node))
{
Interlocked.Increment(ref _hits);
Touch(node);
return node.Value.Wave;
}
if (_negativeWaveIds.ContainsKey(waveId))
{
Interlocked.Increment(ref _hits);
return null;
}
Interlocked.Increment(ref _misses);
lazy = _inflight.GetOrAdd(
waveId,
static (id, self) => new Lazy<WaveData?>(
() => self.DecodeUncached(id),
LazyThreadSafetyMode.ExecutionAndPublication),
this);
}
try
{
WaveData? decoded = lazy.Value;