bcae483made the cache a delivery scheduler (tickets, _deliveryGate turn waits, per-thread delivery queues). It deadlocked inside CultLib: a load's delivery reaching a CultNetDatabaseSubscriptionServer observer that needs _lifecycleGate, while a subscribe's demand handler holding that gate writes the cache and waits for the delivery's turn. The cache now owns order as data. Every admitted change takes the next value of a per-cache in-memory sequence under the gate, published as CultCacheDocumentChange<T>.Sequence. Delivery is the 544c087/b653f68 rule: each call publishes its own changes on its own thread after releasing the gate; nothing waits on another call's delivery. Cross-thread delivery order is not guaranteed. Deleted: ticket counter, _delivered, _deliveryGate, Monitor.Wait turns, per-thread delivery queues, per-ticket catch-all, and the save-order contract text with its observer-must-not-block rule. Consumer: a cache-backed CultMesh document handle hands its observed and reactive mirrors each snapshot's Sequence; ApplyCanonicalSnapshot drops one not newer than the last applied. Tests: SequenceIncreasesInAdmissionOrder, ObserverWritingTheSameCacheDoesNotWaitOnOtherDeliveries, SubscriptionDemandHandlerWritingCacheDuringDeliveryDoesNotDeadlock, WatchRecordIgnoresStaleSequence. The last three time out onbcae483. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
46 lines
1 KiB
C#
46 lines
1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using R3;
|
|
|
|
namespace GameCult.Caching
|
|
{
|
|
public enum CultCacheDocumentChangeKind
|
|
{
|
|
Added,
|
|
|
|
Updated,
|
|
|
|
Removed
|
|
}
|
|
|
|
public sealed class CultCacheDocumentChange<T> where T : class
|
|
{
|
|
public CultCacheDocumentChange(
|
|
CultCacheDocumentChangeKind kind,
|
|
CultRecordKey key,
|
|
T? document,
|
|
T? previousDocument,
|
|
long sequence = 0)
|
|
{
|
|
Kind = kind;
|
|
Key = key;
|
|
Document = document;
|
|
PreviousDocument = previousDocument;
|
|
Sequence = sequence;
|
|
}
|
|
|
|
// Per-cache, in memory, assigned at admission. A consumer keeping a latest value ignores a lower one.
|
|
public long Sequence { get; }
|
|
|
|
public CultCacheDocumentChangeKind Kind { get; }
|
|
|
|
public CultRecordKey Key { get; }
|
|
|
|
public T? Document { get; }
|
|
|
|
public T? PreviousDocument { get; }
|
|
}
|
|
}
|