< Summary

Information
Class: NGql.Core.Pooling.ThreadLocalMemoryManager
Assembly: NGql.Core
File(s): /home/runner/work/NGql/NGql/src/Core/Pooling/ThreadLocalMemoryManager.cs
Line coverage
100%
Covered lines: 46
Uncovered lines: 0
Coverable lines: 46
Total lines: 182
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
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%22100%
RecordThreadLocalHit(...)100%11100%
RecordGlobalPoolHit(...)100%11100%
RecordAllocation(...)100%11100%
GetThreadStats()100%11100%
ResetThreadStats()100%11100%
ReportPoolEfficiency(...)100%11100%
WarmupThreadLocalCaches()100%11100%

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        {
 8724            ThreadLocalHits = threadLocalHits;
 8725            GlobalPoolHits = globalPoolHits;
 8726            Allocations = allocations;
 8727            var totalRequests = threadLocalHits + globalPoolHits + allocations;
 8728            ThreadLocalHitRatio = totalRequests > 0 ? (double)threadLocalHits / totalRequests : 0.0;
 8729        }
 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    {
 754846        _threadLocalHits++;
 754847        PoolingObservability.RecordThreadLocalHit(poolType);
 754848    }
 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    {
 185456        _globalPoolHits++;
 185457        PoolingObservability.RecordGlobalPoolHit(poolType, poolSize);
 185458    }
 59
 60    /// <summary>
 61    /// Records a new allocation with observability
 62    /// </summary>
 63    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 64    internal static void RecordAllocation(string poolType)
 65    {
 123966        _allocations++;
 123967        PoolingObservability.RecordAllocation(poolType);
 123968    }
 69
 70    /// <summary>
 71    /// Gets cache performance statistics for the current thread
 72    /// </summary>
 73    internal static CacheStats GetThreadStats()
 74    {
 7275        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    {
 5783        _threadLocalHits = 0;
 5784        _globalPoolHits = 0;
 5785        _allocations = 0;
 5786    }
 87
 88    /// <summary>
 89    /// Reports pool efficiency for monitoring and alerting
 90    /// </summary>
 91    internal static void ReportPoolEfficiency(string poolType)
 92    {
 993        var stats = GetThreadStats();
 994        var efficiency = new PoolingObservability.PoolEfficiencyStats(
 995            stats.ThreadLocalHits,
 996            stats.GlobalPoolHits,
 997            stats.Allocations);
 98
 999        PoolingObservability.RecordPoolEfficiency(poolType, efficiency);
 9100    }
 101
 102    /// <summary>
 103    /// Warms up thread-local caches with observability tracking
 104    /// </summary>
 105    internal static void WarmupThreadLocalCaches()
 106    {
 6107        using var activity = NGqlActivity.StartQuery("cache_warmup")
 6108            .WithTag("operation.type", "warmup");
 109
 6110        activity.WithObservability(() =>
 6111        {
 6112            // Pre-populate caches with a few instances to avoid allocation spikes
 6113            var dict1 = LockFreeArgumentsPool.GetPooled(new Dictionary<string, object?>());
 6114            var dict2 = LockFreeArgumentsPool.GetPooled(new Dictionary<string, object?>());
 6115            dict1.Dispose();
 6116            dict2.Dispose();
 6117
 6118            var sb1 = LockFreeStringBuilderPool.GetPooled();
 6119            var sb2 = LockFreeStringBuilderPool.GetPooled();
 6120            sb1.Dispose();
 6121            sb2.Dispose();
 6122
 6123            var set1 = LockFreeHashSetPool.GetPooled();
 6124            set1.Dispose();
 12125        }, "warmup_caches");
 12126    }
 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
 143        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    {
 152        var newNode = new Node(item);
 153        Node? currentHead;
 154        do
 155        {
 156            currentHead = _head;
 157            newNode.Next = currentHead;
 158        } while (Interlocked.CompareExchange(ref _head, newNode, currentHead) != currentHead);
 159    }
 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        {
 170            currentHead = _head;
 171            if (currentHead == null)
 172            {
 173                item = default!;
 174                return false;
 175            }
 176        } while (Interlocked.CompareExchange(ref _head, currentHead.Next, currentHead) != currentHead);
 177
 178        item = currentHead.Item;
 179        ThreadLocalMemoryManager.RecordGlobalPoolHit("unknown", -1);
 180        return true;
 181    }
 182}