fix: complete retail parity stability pass
This commit is contained in:
parent
d3df4cb20a
commit
f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue