| File: System\Text\Json\Serialization\Metadata\ReflectionEmitCachingMemberAccessor.Cache.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #if NETFRAMEWORK || NET using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading; namespace System.Text.Json.Serialization.Metadata { internal sealed partial class ReflectionEmitCachingMemberAccessor { private sealed class Cache<TKey> where TKey : notnull { private int _evictLock; private long _lastEvictedTimestamp; // Stopwatch timestamp of the latest eviction operation. private readonly TimeSpan _evictionInterval; // min duration needed to trigger a new evict operation. private readonly TimeSpan _slidingExpiration; // max duration allowed for cache entries to remain inactive. private readonly ConcurrentDictionary<TKey, CacheEntry> _cache = new(); public Cache(TimeSpan slidingExpiration, TimeSpan evictionInterval) { _slidingExpiration = slidingExpiration; _evictionInterval = evictionInterval; _lastEvictedTimestamp = Stopwatch.GetTimestamp(); } public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where TValue : class? { CacheEntry entry = _cache.GetOrAdd( key, #if NET static (TKey key, Func<TKey, TValue> valueFactory) => new(valueFactory(key)), valueFactory); #else key => new(valueFactory(key))); #endif long nowTimestamp = Stopwatch.GetTimestamp(); Volatile.Write(ref entry.LastUsedTimestamp, nowTimestamp); if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictionInterval) { if (Interlocked.CompareExchange(ref _evictLock, 1, 0) == 0) { if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictionInterval) { EvictStaleCacheEntries(nowTimestamp); Volatile.Write(ref _lastEvictedTimestamp, nowTimestamp); } Volatile.Write(ref _evictLock, 0); } } return (TValue)entry.Value!; } public void Clear() { _cache.Clear(); Volatile.Write(ref _lastEvictedTimestamp, Stopwatch.GetTimestamp()); } private void EvictStaleCacheEntries(long nowTimestamp) { foreach (KeyValuePair<TKey, CacheEntry> kvp in _cache) { if (Stopwatch.GetElapsedTime(Volatile.Read(ref kvp.Value.LastUsedTimestamp), nowTimestamp) >= _slidingExpiration) { _cache.TryRemove(kvp.Key, out _); } } } private sealed class CacheEntry { public readonly object? Value; public long LastUsedTimestamp; public CacheEntry(object? value) { Value = value; } } } } } #endif