< Summary

Line coverage
71%
Covered lines: 59
Uncovered lines: 23
Coverable lines: 82
Total lines: 166
Line coverage: 71.9%
Branch coverage
100%
Covered branches: 12
Total branches: 12
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\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 _lastEvictedTicks; // timestamp of latest eviction operation.
 18            private readonly long _evictionIntervalTicks; // min timespan needed to trigger a new evict operation.
 19            private readonly long _slidingExpirationTicks; // max timespan 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                _slidingExpirationTicks = slidingExpiration.Ticks;
 125                _evictionIntervalTicks = evictionInterval.Ticks;
 126                _lastEvictedTicks = DateTime.UtcNow.Ticks;
 127            }
 28
 29            public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where TValue : class?
 2859130            {
 2859131                CacheEntry entry = _cache.GetOrAdd(
 2859132                    key,
 2859133#if NET
 1951034                    static (TKey key, Func<TKey, TValue> valueFactory) => new(valueFactory(key)),
 2859135                    valueFactory);
 36#else
 37                    key => new(valueFactory(key)));
 38#endif
 2859139                long utcNowTicks = DateTime.UtcNow.Ticks;
 2859140                Volatile.Write(ref entry.LastUsedTicks, utcNowTicks);
 41
 2859142                if (utcNowTicks - Volatile.Read(ref _lastEvictedTicks) >= _evictionIntervalTicks)
 55243                {
 55244                    if (Interlocked.CompareExchange(ref _evictLock, 1, 0) == 0)
 55245                    {
 55246                        if (utcNowTicks - _lastEvictedTicks >= _evictionIntervalTicks)
 55247                        {
 55248                            EvictStaleCacheEntries(utcNowTicks);
 55249                            Volatile.Write(ref _lastEvictedTicks, utcNowTicks);
 55250                        }
 51
 55252                        Volatile.Write(ref _evictLock, 0);
 55253                    }
 55254                }
 55
 2859156                return (TValue)entry.Value!;
 2859157            }
 58
 59            public void Clear()
 060            {
 061                _cache.Clear();
 062                _lastEvictedTicks = DateTime.UtcNow.Ticks;
 063            }
 64
 65            private void EvictStaleCacheEntries(long utcNowTicks)
 55266            {
 20681667                foreach (KeyValuePair<TKey, CacheEntry> kvp in _cache)
 10258068                {
 10258069                    if (utcNowTicks - Volatile.Read(ref kvp.Value.LastUsedTicks) >= _slidingExpirationTicks)
 1923970                    {
 1923971                        _cache.TryRemove(kvp.Key, out _);
 1923972                    }
 10258073                }
 55274            }
 75
 76            private sealed class CacheEntry
 77            {
 78                public readonly object? Value;
 79                public long LastUsedTicks;
 80
 1951081                public CacheEntry(object? value)
 1951082                {
 1951083                    Value = value;
 1951084                }
 85            }
 86        }
 87    }
 88}
 89#endif

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\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) =>
 1225432            _cache.GetOrAdd(
 1225433                key: (nameof(CreateParameterlessConstructor), type, ctorInfo),
 1559434                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) =>
 74957            _cache.GetOrAdd(
 74958                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
 148759                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, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo) =>
 779467            _cache.GetOrAdd(
 779468                key: (nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo),
 1551069                valueFactory: key => _sourceAccessor.CreatePropertyGetter<TProperty>((PropertyInfo)key.member!));
 70
 71        public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo) =>
 779472            _cache.GetOrAdd(
 779473                key: (nameof(CreatePropertySetter), typeof(TProperty), propertyInfo),
 1551074                valueFactory: key => _sourceAccessor.CreatePropertySetter<TProperty>((PropertyInfo)key.member!));
 75    }
 76}
 77#endif