perf(content): close prepared asset cutover
This commit is contained in:
parent
b1ad4b7c0a
commit
a564c4b782
11 changed files with 428 additions and 66 deletions
|
|
@ -5,14 +5,15 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
|
||||
namespace AcDream.Content;
|
||||
|
||||
/// <summary>
|
||||
/// THE <see cref="DatCollection"/> → <see cref="IDatReaderWriter"/> adapter,
|
||||
/// shared by all three consumers: ObjectMeshManager (AcDream.App, via
|
||||
/// WbMeshAdapter), acdream-bake (AcDream.Bake.BakeRunner), and the pak
|
||||
/// equivalence suite (AcDream.Content.Tests). MP1b's review found the
|
||||
/// shared by runtime DAT-backed gameplay/content access,
|
||||
/// acdream-bake (AcDream.Bake.BakeRunner), and the pak equivalence suite
|
||||
/// (AcDream.Content.Tests). MP1b's review found the
|
||||
/// original App-internal copy had silently forked into three near-identical
|
||||
/// versions — drift between them is exactly what the live-vs-pak equivalence
|
||||
/// suite CANNOT detect (both sides would drift together only if they share
|
||||
|
|
@ -218,6 +219,7 @@ public sealed class DatDatabaseWrapper : IDatDatabase {
|
|||
// cache itself documents why estimated bytes are not a hard heap bound.
|
||||
private readonly BoundedDatObjectCache _cache = new();
|
||||
private readonly object _databaseLock = new();
|
||||
private int _malformedRecordDiagnosticEmitted;
|
||||
|
||||
public DatDatabaseWrapper(DatDatabase db) {
|
||||
ArgumentNullException.ThrowIfNull(db);
|
||||
|
|
@ -241,6 +243,7 @@ public sealed class DatDatabaseWrapper : IDatDatabase {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool existingRecordFailed;
|
||||
lock (_databaseLock) {
|
||||
// A different reader may have populated the cache while this
|
||||
// caller waited for the serialized DatDatabase read path.
|
||||
|
|
@ -253,17 +256,25 @@ public sealed class DatDatabaseWrapper : IDatDatabase {
|
|||
return true;
|
||||
}
|
||||
|
||||
// TEMP diagnostic (dat-race investigation 2026-06-09, strip with fix):
|
||||
// a miss for an id whose BTree entry EXISTS is always an anomaly —
|
||||
// either Unpack returned false or the lookup flickered transiently.
|
||||
// Legit not-found probes (e.g. Portal→HighRes fallback) stay silent.
|
||||
// Kept through the MP1b unification — the tripwire now covers the
|
||||
// bake tool and the equivalence suite too, not just the client.
|
||||
if (_db.Tree.TryGetFile(fileId, out _)) {
|
||||
Console.WriteLine(
|
||||
$"[dat-miss] {typeof(T).Name} 0x{fileId:X8} entry EXISTS but TryGet failed " +
|
||||
$"(thread={Environment.CurrentManagedThreadId})");
|
||||
}
|
||||
// A miss for an id whose BTree entry exists is a typed-access
|
||||
// anomaly (wrong requested type, malformed content, or an unpacker
|
||||
// failure). Capture only the fact while serialized DAT access is
|
||||
// held; diagnostics must never perform console I/O under this
|
||||
// shared lock.
|
||||
existingRecordFailed = _db.Tree.TryGetFile(fileId, out _);
|
||||
}
|
||||
|
||||
// One actionable report per database keeps a repeatedly requested
|
||||
// malformed record from becoming another exception/logging-style hot
|
||||
// path. Missing keys stay silent.
|
||||
if (existingRecordFailed
|
||||
&& Interlocked.Exchange(
|
||||
ref _malformedRecordDiagnosticEmitted,
|
||||
1) == 0)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[dat-miss] {typeof(T).Name} 0x{fileId:X8} entry EXISTS but TryGet failed " +
|
||||
$"(thread={Environment.CurrentManagedThreadId}; further reports suppressed)");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue