< Summary

Line coverage
66%
Covered lines: 59
Uncovered lines: 30
Coverable lines: 89
Total lines: 184
Line coverage: 66.2%
Branch coverage
80%
Covered branches: 8
Total branches: 10
Branch coverage: 80%
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.
 120            private readonly ConcurrentDictionary<TKey, CacheEntry> _cache = new();
 21
 122            public Cache(TimeSpan slidingExpiration, TimeSpan evictionInterval)
 123            {
 124                _slidingExpiration = slidingExpiration;
 125                _evictionInterval = evictionInterval;
 126                _lastEvictedTimestamp = Stopwatch.GetTimestamp();
 127            }
 28
 29            public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where TValue : class?
 1043230            {
 1043231                CacheEntry entry = _cache.GetOrAdd(
 1043232                    key,
 1043233#if NET
 720534                    static (TKey key, Func<TKey, TValue> valueFactory) => new(valueFactory(key)),
 1043235                    valueFactory);
 36#else
 37                    key => new(valueFactory(key)));
 38#endif
 1043239                long nowTimestamp = Stopwatch.GetTimestamp();
 1043240                Volatile.Write(ref entry.LastUsedTimestamp, nowTimestamp);
 41
 1043242                if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictionInterva
 18143                {
 18144                    if (Interlocked.CompareExchange(ref _evictLock, 1, 0) == 0)
 18145                    {
 18146                        if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictio
 18147                        {
 18148                            EvictStaleCacheEntries(nowTimestamp);
 18149                            Volatile.Write(ref _lastEvictedTimestamp, nowTimestamp);
 18150                        }
 51
 18152                        Volatile.Write(ref _evictLock, 0);
 18153                    }
 18154                }
 55
 1043256                return (TValue)entry.Value!;
 1043257            }
 58
 59            public void Clear()
 060            {
 061                _cache.Clear();
 062                Volatile.Write(ref _lastEvictedTimestamp, Stopwatch.GetTimestamp());
 063            }
 64
 65            private void EvictStaleCacheEntries(long nowTimestamp)
 18166            {
 8205367                foreach (KeyValuePair<TKey, CacheEntry> kvp in _cache)
 4075568                {
 4075569                    if (Stopwatch.GetElapsedTime(Volatile.Read(ref kvp.Value.LastUsedTimestamp), nowTimestamp) >= _slidi
 695470                    {
 695471                        _cache.TryRemove(kvp.Key, out _);
 695472                    }
 4075573                }
 18174            }
 75
 76            private sealed class CacheEntry
 77            {
 78                public readonly object? Value;
 79                public long LastUsedTimestamp;
 80
 720581                public CacheEntry(object? value)
 720582                {
 720583                    Value = value;
 720584                }
 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)]
 118        public ReflectionEmitCachingMemberAccessor()
 119        {
 120            _sourceAccessor = new ReflectionEmitMemberAccessor();
 121            _cache = new(slidingExpiration: TimeSpan.FromMilliseconds(1000), evictionInterval: TimeSpan.FromMilliseconds
 122        }
 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) =>
 414732            _cache.GetOrAdd(
 414733                key: (nameof(CreateParameterlessConstructor), type, ctorInfo),
 521834                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) =>
 33357            _cache.GetOrAdd(
 33358                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
 65959                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) =>
 297672            _cache.GetOrAdd(
 297673                key: (nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo),
 588074                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) =>
 297682            _cache.GetOrAdd(
 297683                key: (nameof(CreatePropertySetter), typeof(TProperty), propertyInfo),
 588084                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