< Summary

Information
Class: NGql.Core.Pooling.MonitoredLockFreeStack<T>
Assembly: NGql.Core
File(s): /home/runner/work/NGql/NGql/src/Core/Pooling/ThreadLocalMemoryManager.cs
Line coverage
100%
Covered lines: 14
Uncovered lines: 0
Coverable lines: 14
Total lines: 182
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Push(...)100%22100%
TryPop(...)100%44100%

File(s)

/home/runner/work/NGql/NGql/src/Core/Pooling/ThreadLocalMemoryManager.cs

#LineLine coverage
 1using System.Runtime.CompilerServices;
 2using NGql.Core.Observability;
 3
 4namespace NGql.Core.Pooling;
 5
 6/// <summary>
 7/// Centralized thread-local memory management with integrated observability.
 8/// Provides thread-affinity optimizations and comprehensive telemetry.
 9/// </summary>
 10internal static class ThreadLocalMemoryManager
 11{
 12    /// <summary>
 13    /// Provides statistics about thread-local cache efficiency
 14    /// </summary>
 15    public readonly struct CacheStats
 16    {
 17        public readonly long ThreadLocalHits;
 18        public readonly long GlobalPoolHits;
 19        public readonly long Allocations;
 20        public readonly double ThreadLocalHitRatio;
 21
 22        internal CacheStats(long threadLocalHits, long globalPoolHits, long allocations)
 23        {
 24            ThreadLocalHits = threadLocalHits;
 25            GlobalPoolHits = globalPoolHits;
 26            Allocations = allocations;
 27            var totalRequests = threadLocalHits + globalPoolHits + allocations;
 28            ThreadLocalHitRatio = totalRequests > 0 ? (double)threadLocalHits / totalRequests : 0.0;
 29        }
 30    }
 31
 32    // Thread-local counters for performance monitoring
 33    [ThreadStatic]
 34    private static long _threadLocalHits;
 35    [ThreadStatic]
 36    private static long _globalPoolHits;
 37    [ThreadStatic]
 38    private static long _allocations;
 39
 40    /// <summary>
 41    /// Records a thread-local cache hit with observability
 42    /// </summary>
 43    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 44    internal static void RecordThreadLocalHit(string poolType)
 45    {
 46        _threadLocalHits++;
 47        PoolingObservability.RecordThreadLocalHit(poolType);
 48    }
 49
 50    /// <summary>
 51    /// Records a global pool hit with observability
 52    /// </summary>
 53    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 54    internal static void RecordGlobalPoolHit(string poolType, int poolSize = -1)
 55    {
 56        _globalPoolHits++;
 57        PoolingObservability.RecordGlobalPoolHit(poolType, poolSize);
 58    }
 59
 60    /// <summary>
 61    /// Records a new allocation with observability
 62    /// </summary>
 63    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 64    internal static void RecordAllocation(string poolType)
 65    {
 66        _allocations++;
 67        PoolingObservability.RecordAllocation(poolType);
 68    }
 69
 70    /// <summary>
 71    /// Gets cache performance statistics for the current thread
 72    /// </summary>
 73    internal static CacheStats GetThreadStats()
 74    {
 75        return new CacheStats(_threadLocalHits, _globalPoolHits, _allocations);
 76    }
 77
 78    /// <summary>
 79    /// Resets performance counters for the current thread (useful for benchmarking)
 80    /// </summary>
 81    internal static void ResetThreadStats()
 82    {
 83        _threadLocalHits = 0;
 84        _globalPoolHits = 0;
 85        _allocations = 0;
 86    }
 87
 88    /// <summary>
 89    /// Reports pool efficiency for monitoring and alerting
 90    /// </summary>
 91    internal static void ReportPoolEfficiency(string poolType)
 92    {
 93        var stats = GetThreadStats();
 94        var efficiency = new PoolingObservability.PoolEfficiencyStats(
 95            stats.ThreadLocalHits,
 96            stats.GlobalPoolHits,
 97            stats.Allocations);
 98
 99        PoolingObservability.RecordPoolEfficiency(poolType, efficiency);
 100    }
 101
 102    /// <summary>
 103    /// Warms up thread-local caches with observability tracking
 104    /// </summary>
 105    internal static void WarmupThreadLocalCaches()
 106    {
 107        using var activity = NGqlActivity.StartQuery("cache_warmup")
 108            .WithTag("operation.type", "warmup");
 109
 110        activity.WithObservability(() =>
 111        {
 112            // Pre-populate caches with a few instances to avoid allocation spikes
 113            var dict1 = LockFreeArgumentsPool.GetPooled(new Dictionary<string, object?>());
 114            var dict2 = LockFreeArgumentsPool.GetPooled(new Dictionary<string, object?>());
 115            dict1.Dispose();
 116            dict2.Dispose();
 117
 118            var sb1 = LockFreeStringBuilderPool.GetPooled();
 119            var sb2 = LockFreeStringBuilderPool.GetPooled();
 120            sb1.Dispose();
 121            sb2.Dispose();
 122
 123            var set1 = LockFreeHashSetPool.GetPooled();
 124            set1.Dispose();
 125        }, "warmup_caches");
 126    }
 127
 128}
 129
 130/// <summary>
 131/// Enhanced lock-free stack with performance monitoring integration
 132/// </summary>
 133/// <typeparam name="T">Type of items in the stack</typeparam>
 134internal sealed class MonitoredLockFreeStack<T> where T : class
 135{
 136    private volatile Node? _head;
 137
 138    private sealed class Node
 139    {
 140        public readonly T Item;
 141        public Node? Next;
 142
 4758143        public Node(T item) => Item = item;
 144    }
 145
 146    /// <summary>
 147    /// Pushes an item onto the stack using compare-and-swap
 148    /// </summary>
 149    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 150    public void Push(T item)
 151    {
 2379152        var newNode = new Node(item);
 153        Node? currentHead;
 154        do
 155        {
 2382156            currentHead = _head;
 2382157            newNode.Next = currentHead;
 2382158        } while (Interlocked.CompareExchange(ref _head, newNode, currentHead) != currentHead);
 2379159    }
 160
 161    /// <summary>
 162    /// Attempts to pop an item from the stack using compare-and-swap with monitoring
 163    /// </summary>
 164    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 165    public bool TryPop(out T item)
 166    {
 167        Node? currentHead;
 168        do
 169        {
 33051170            currentHead = _head;
 33051171            if (currentHead == null)
 172            {
 31233173                item = default!;
 31233174                return false;
 175            }
 1818176        } while (Interlocked.CompareExchange(ref _head, currentHead.Next, currentHead) != currentHead);
 177
 1818178        item = currentHead.Item;
 1818179        ThreadLocalMemoryManager.RecordGlobalPoolHit("unknown", -1);
 1818180        return true;
 181    }
 182}

Methods/Properties

.ctor(T)
Push(T)
TryPop(T&)