< Summary

Line coverage
65%
Covered lines: 58
Uncovered lines: 30
Coverable lines: 88
Total lines: 178
Line coverage: 65.9%
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)

D:\runner\runtime\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?
 259430            {
 259431                CacheEntry entry = _cache.GetOrAdd(
 259432                    key,
 157233                    static (TKey key, Func<TKey, TValue> valueFactory) => new(valueFactory(key)),
 259434                    valueFactory);
 259435                long nowTimestamp = Stopwatch.GetTimestamp();
 259436                Volatile.Write(ref entry.LastUsedTimestamp, nowTimestamp);
 37
 259438                if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictionInterva
 6739                {
 6740                    if (Interlocked.CompareExchange(ref _evictLock, 1, 0) == 0)
 6741                    {
 6742                        if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictio
 6743                        {
 6744                            EvictStaleCacheEntries(nowTimestamp);
 6745                            Volatile.Write(ref _lastEvictedTimestamp, nowTimestamp);
 6746                        }
 47
 6748                        Volatile.Write(ref _evictLock, 0);
 6749                    }
 6750                }
 51
 259452                return (TValue)entry.Value!;
 259453            }
 54
 55            public void Clear()
 056            {
 057                _cache.Clear();
 058                Volatile.Write(ref _lastEvictedTimestamp, Stopwatch.GetTimestamp());
 059            }
 60
 61            private void EvictStaleCacheEntries(long nowTimestamp)
 6762            {
 1902363                foreach (KeyValuePair<TKey, CacheEntry> kvp in _cache)
 941164                {
 941165                    if (Stopwatch.GetElapsedTime(Volatile.Read(ref kvp.Value.LastUsedTimestamp), nowTimestamp) >= _slidi
 140466                    {
 140467                        _cache.TryRemove(kvp.Key, out _);
 140468                    }
 941169                }
 6770            }
 71
 72            private sealed class CacheEntry
 73            {
 74                public readonly object? Value;
 75                public long LastUsedTimestamp;
 76
 157277                public CacheEntry(object? value)
 157278                {
 157279                    Value = value;
 157280                }
 81            }
 82        }
 83    }
 84}
 85#endif

D:\runner\runtime\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) =>
 124732            _cache.GetOrAdd(
 124733                key: (nameof(CreateParameterlessConstructor), type, ctorInfo),
 153734                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) =>
 9957            _cache.GetOrAdd(
 9958                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
 19359                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) =>
 62472            _cache.GetOrAdd(
 62473                key: (nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo),
 121874                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) =>
 62482            _cache.GetOrAdd(
 62483                key: (nameof(CreatePropertySetter), typeof(TProperty), propertyInfo),
 121884                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