< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 89
Coverable lines: 89
Total lines: 184
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.Cache.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4#if NETFRAMEWORK || NET
 5using System.Collections.Concurrent;
 6using System.Collections.Generic;
 7using System.Diagnostics;
 8using System.Threading;
 9
 10namespace System.Text.Json.Serialization.Metadata
 11{
 12    internal sealed partial class ReflectionEmitCachingMemberAccessor
 13    {
 14        private sealed class Cache<TKey> where TKey : notnull
 15        {
 16            private int _evictLock;
 17            private long _lastEvictedTimestamp; // Stopwatch timestamp of the latest eviction operation.
 18            private readonly TimeSpan _evictionInterval; // min duration needed to trigger a new evict operation.
 19            private readonly TimeSpan _slidingExpiration; // max duration allowed for cache entries to remain inactive.
 020            private readonly ConcurrentDictionary<TKey, CacheEntry> _cache = new();
 21
 022            public Cache(TimeSpan slidingExpiration, TimeSpan evictionInterval)
 023            {
 024                _slidingExpiration = slidingExpiration;
 025                _evictionInterval = evictionInterval;
 026                _lastEvictedTimestamp = Stopwatch.GetTimestamp();
 027            }
 28
 29            public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where TValue : class?
 030            {
 031                CacheEntry entry = _cache.GetOrAdd(
 032                    key,
 033#if NET
 034                    static (TKey key, Func<TKey, TValue> valueFactory) => new(valueFactory(key)),
 035                    valueFactory);
 36#else
 37                    key => new(valueFactory(key)));
 38#endif
 039                long nowTimestamp = Stopwatch.GetTimestamp();
 040                Volatile.Write(ref entry.LastUsedTimestamp, nowTimestamp);
 41
 042                if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictionInterva
 043                {
 044                    if (Interlocked.CompareExchange(ref _evictLock, 1, 0) == 0)
 045                    {
 046                        if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictio
 047                        {
 048                            EvictStaleCacheEntries(nowTimestamp);
 049                            Volatile.Write(ref _lastEvictedTimestamp, nowTimestamp);
 050                        }
 51
 052                        Volatile.Write(ref _evictLock, 0);
 053                    }
 054                }
 55
 056                return (TValue)entry.Value!;
 057            }
 58
 59            public void Clear()
 060            {
 061                _cache.Clear();
 062                Volatile.Write(ref _lastEvictedTimestamp, Stopwatch.GetTimestamp());
 063            }
 64
 65            private void EvictStaleCacheEntries(long nowTimestamp)
 066            {
 067                foreach (KeyValuePair<TKey, CacheEntry> kvp in _cache)
 068                {
 069                    if (Stopwatch.GetElapsedTime(Volatile.Read(ref kvp.Value.LastUsedTimestamp), nowTimestamp) >= _slidi
 070                    {
 071                        _cache.TryRemove(kvp.Key, out _);
 072                    }
 073                }
 074            }
 75
 76            private sealed class CacheEntry
 77            {
 78                public readonly object? Value;
 79                public long LastUsedTimestamp;
 80
 081                public CacheEntry(object? value)
 082                {
 083                    Value = value;
 084                }
 85            }
 86        }
 87    }
 88}
 89#endif
 90

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4#if NETFRAMEWORK || NET
 5using System.Collections.Generic;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8
 9namespace System.Text.Json.Serialization.Metadata
 10{
 11    internal sealed partial class ReflectionEmitCachingMemberAccessor : MemberAccessor
 12    {
 13        private readonly ReflectionEmitMemberAccessor _sourceAccessor;
 14        private readonly Cache<(string id, Type declaringType, MemberInfo? member)> _cache;
 15
 16        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 17        [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 018        public ReflectionEmitCachingMemberAccessor()
 019        {
 020            _sourceAccessor = new ReflectionEmitMemberAccessor();
 021            _cache = new(slidingExpiration: TimeSpan.FromMilliseconds(1000), evictionInterval: TimeSpan.FromMilliseconds
 022        }
 23
 024        public override void Clear() => _cache.Clear();
 25
 26        public override Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAcce
 027            _cache.GetOrAdd(
 028                key: (nameof(CreateAddMethodDelegate), typeof(TCollection), null),
 029                _ => _sourceAccessor.CreateAddMethodDelegate<TCollection>());
 30
 31        public override Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? ctorInfo) =>
 032            _cache.GetOrAdd(
 033                key: (nameof(CreateParameterlessConstructor), type, ctorInfo),
 034                valueFactory: key => _sourceAccessor.CreateParameterlessConstructor(key.declaringType, (ConstructorInfo?
 35
 36        public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo) =>
 037            _cache.GetOrAdd(
 038                key: (nameof(CreateFieldGetter), typeof(TProperty), fieldInfo),
 039                valueFactory: key => _sourceAccessor.CreateFieldGetter<TProperty>((FieldInfo)key.member!));
 40
 41        public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo) =>
 042            _cache.GetOrAdd(
 043                key: (nameof(CreateFieldSetter), typeof(TProperty), fieldInfo),
 044                valueFactory: key => _sourceAccessor.CreateFieldSetter<TProperty>((FieldInfo)key.member!));
 45
 46        public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeD
 047            _cache.GetOrAdd(
 048                key: (nameof(CreateImmutableDictionaryCreateRangeDelegate), typeof((TCollection, TKey, TValue)), null),
 049                valueFactory: _ => _sourceAccessor.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValu
 50
 51        public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollectio
 052            _cache.GetOrAdd(
 053                key: (nameof(CreateImmutableEnumerableCreateRangeDelegate), typeof((TCollection, TElement)), null),
 054                valueFactory: _ => _sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>()
 55
 56        public override Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
 057            _cache.GetOrAdd(
 058                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
 059                valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T>((ConstructorInfo)key.member!));
 60
 61        public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>? CreateParameterize
 062            _cache.GetOrAdd(
 063                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
 064                valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>((Cons
 65
 66        public override Func<object?, T> CreateSingleParameterConstructor<T>(ConstructorInfo constructor) =>
 067            _cache.GetOrAdd(
 068                key: (nameof(CreateSingleParameterConstructor), typeof(T), constructor),
 069                valueFactory: key => _sourceAccessor.CreateSingleParameterConstructor<T>((ConstructorInfo)key.member!));
 70
 71        public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo) =>
 072            _cache.GetOrAdd(
 073                key: (nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo),
 074                valueFactory: key => _sourceAccessor.CreatePropertyGetter<TProperty>((PropertyInfo)key.member!));
 75
 76        public override Func<TDeclaringType, TProperty> CreatePropertyGetter<TDeclaringType, TProperty>(PropertyInfo pro
 077            _cache.GetOrAdd(
 078                key: ("CreatePropertyGetter`2", typeof((TDeclaringType, TProperty)), propertyInfo),
 079                valueFactory: key => _sourceAccessor.CreatePropertyGetter<TDeclaringType, TProperty>((PropertyInfo)key.m
 80
 81        public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo) =>
 082            _cache.GetOrAdd(
 083                key: (nameof(CreatePropertySetter), typeof(TProperty), propertyInfo),
 084                valueFactory: key => _sourceAccessor.CreatePropertySetter<TProperty>((PropertyInfo)key.member!));
 85
 86        public override UnionTryGetValueAccessor<TUnion> CreateUnionTryGetValueAccessor<TUnion>(IReadOnlyList<KeyValuePa
 87            // The chained accessor is built once per JsonTypeInfo<TUnion> during metadata
 88            // population, so an additional cache layer would not pay for itself; forward
 89            // directly to the underlying emit-backed accessor.
 090            _sourceAccessor.CreateUnionTryGetValueAccessor<TUnion>(entries);
 91    }
 92}
 93#endif
 94