namespace Explorer.Application; public readonly record struct ThumbnailKey(string Path, long ModifiedTicks, int PixelWidth) { public static ThumbnailKey From(string path, DateTimeOffset? modifiedUtc, int pixelWidth) => new(path, modifiedUtc?.UtcTicks ?? 0, pixelWidth); } public readonly record struct ThumbnailJob( ThumbnailKey Key, int Rank, int Generation); public readonly record struct ThumbnailSchedulerStats( int Generation, int Window, int Pending, int InFlight, int Requested, int CacheHits, int Generated, int Cancelled, int Failed); public sealed class ThumbnailScheduler { public const int DefaultConcurrency = 2; public const int DefaultPixelWidth = 128; public const int MaxPending = 96; public const int PrefetchItems = 24; private readonly object _gate = new(); private readonly List _window = []; private readonly HashSet _windowKeys = []; private readonly HashSet _inFlight = []; private readonly HashSet _cached = []; private int _generation; private bool _enabled; private int _requested; private int _cacheHits; private int _generated; private int _cancelled; private int _failed; public int Generation { get { lock (_gate) return _generation; } } public int InFlight { get { lock (_gate) return _inFlight.Count; } } public void Reset(int generation) { lock (_gate) { _cancelled += _window.Count + _inFlight.Count; _generation = generation; _window.Clear(); _windowKeys.Clear(); _inFlight.Clear(); } } public void SetEnabled(bool enabled) { lock (_gate) { _enabled = enabled; if (enabled) { return; } _cancelled += _window.Count + _inFlight.Count; _window.Clear(); _windowKeys.Clear(); _inFlight.Clear(); } } public bool IsCached(ThumbnailKey key) { lock (_gate) { return _cached.Contains(key); } } public void MarkCached(ThumbnailKey key) { lock (_gate) { _cached.Add(key); } } public void RecordCacheHit() { lock (_gate) { _cacheHits++; _requested++; } } public IReadOnlyList SetWindow(int generation, IReadOnlyList jobs) { ArgumentNullException.ThrowIfNull(jobs); lock (_gate) { if (generation != _generation || !_enabled) { _cancelled += jobs.Count; return []; } var next = jobs .Where(j => j.Generation == _generation) .GroupBy(j => j.Key) .Select(g => g.OrderBy(j => j.Rank).First()) .OrderBy(j => j.Rank) .Take(MaxPending) .ToList(); var nextKeys = next.Select(j => j.Key).ToHashSet(); foreach (var previous in _window) { if (!nextKeys.Contains(previous.Key)) { _cancelled++; } } foreach (var key in _inFlight.ToArray()) { if (!nextKeys.Contains(key)) { _inFlight.Remove(key); _cancelled++; } } _window.Clear(); _window.AddRange(next); _windowKeys.Clear(); foreach (var job in next) { _windowKeys.Add(job.Key); } _requested += next.Count(j => !_cached.Contains(j.Key) && !_inFlight.Contains(j.Key)); return next; } } public bool TryTake(out ThumbnailJob job) { lock (_gate) { if (!_enabled) { job = default; return false; } foreach (var candidate in _window) { if (_cached.Contains(candidate.Key) || _inFlight.Contains(candidate.Key)) { continue; } _inFlight.Add(candidate.Key); job = candidate; return true; } } job = default; return false; } public bool IsCurrent(ThumbnailJob job) { lock (_gate) { return _enabled && job.Generation == _generation && _windowKeys.Contains(job.Key); } } public void Complete(ThumbnailJob job, bool generated, bool failed) { lock (_gate) { _inFlight.Remove(job.Key); if (failed) { _failed++; _cached.Add(job.Key); return; } if (generated) { _generated++; _cached.Add(job.Key); } } } public ThumbnailSchedulerStats Snapshot() { lock (_gate) { var pending = _window.Count(j => !_cached.Contains(j.Key) && !_inFlight.Contains(j.Key)); return new ThumbnailSchedulerStats( _generation, _window.Count, pending, _inFlight.Count, _requested, _cacheHits, _generated, _cancelled, _failed); } } }