// ----------------------------------------------------------------------------- // file="L4.Predict" // Project: StgSharp // Copyright (c) Nitload. // SPDX-License-Identifier: MIT // ----------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Nitload.Common.HighPerformance.Memory { public unsafe partial class L4 { private int _active_line_count; private int _last_evict_pos; internal partial int Predict() { if (!TryGetCurrentPredictor(out IL4Predictor? p)) { return 0; } int total_count = 0, count; Span as_sp = stackalloc L4.AddressSource[4]; SwissEnumerator e = _addressMap.GetWriteBufferEnumerator(); Predict: while ((count = p.PredictMission(in as_sp)) > 0) { for (int i = 0; i < count; i++) { if (_addressMap.TryGetWrite(as_sp[i], out ulong map_handle_adr)) { L4.CacheLineMetadata* map_handle = (L4.CacheLineMetadata*)map_handle_adr; map_handle->IncreasePredictCount(); map_handle->CheckCount(); } else { uint size = as_sp[i].Size; int handle_idx = _bufferAllocator.Alloc(size); L4.CacheLineMetadata* handle = _bufferAllocator.MetadataAt(handle_idx); p.Prefetch(as_sp[i], handle->Position, handle->SizeMask); if (!_addressMap.TryAddOrSet(as_sp[i], (ulong)handle)) { throw new InvalidOperationException(); } _active_line_count++; if (_active_line_count >= MaxActiveCacheLineCount) { int retry = 0; const int max_retry = 3; for (; retry < max_retry && EvictOnce(ref e); retry++) { e = _addressMap.GetWriteBufferEnumerator(_last_evict_pos); } if (retry >= max_retry) { throw new OverflowException("Evict failed, maybe all cache lines are in use."); } _active_line_count--; } } } total_count += count; if (total_count >= 2048) { _ = Interlocked.Add(ref _predict_count, total_count); _last_evict_pos = e.CurrentOffset; return total_count; } } if (MoveNextPredictor()) { _ = TryGetCurrentPredictor(out p); goto Predict; } _ = Interlocked.Add(ref _predict_count, total_count); _last_evict_pos = e.CurrentOffset; return total_count; } private bool EvictOnce( ref SwissEnumerator e ) { while (e.MoveNext()) { (AddressSource source, ulong cache_head_ptr) = e.Current; L4.CacheLineMetadata* handle = (L4.CacheLineMetadata*)cache_head_ptr; if (Volatile.Read(ref handle->ConsumeCount) == Volatile.Read(ref handle->ConsumeCount)) { // evict _ = _addressMap.TryRemove(source, out _); _bufferAllocator.Free(handle); return true; } } return false; } } } // Eviction follow-up: // 1. Compare ConsumeCount with PredictCount; reset both on TLSF slot reuse and count new predictions. // 2. Fix retry/result handling; decrement _active_line_count only after a successful eviction. // 3. Correct TLSF reverse-slab pointer validation before freeing metadata pointers. // 4. Defer TLSF Free until retired published buffers and outstanding handles can no longer reference the line. // 5. Add Swiss TryRemoveAt(offset) to tombstone the enumerated slot without hashing and probing again.