Skip to content

Commit fb0029c

Browse files
committed
fix: use Stopwatch for ReflectionEmitCachingMemberAccessor TTL
1 parent 9dced0c commit fb0029c

2 files changed

Lines changed: 18 additions & 17 deletions

File tree

src/libraries/System.Text.Json/src/System.Text.Json.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
372372
<Compile Include="$(CommonPath)Polyfills\BitConverterPolyfills.cs" Link="Common\Polyfills\BitConverterPolyfills.cs" />
373373
<Compile Include="$(CommonPath)Polyfills\ArrayPolyfills.cs" Link="Common\Polyfills\ArrayPolyfills.cs" />
374374
<Compile Include="$(CommonPath)Polyfills\RuntimeHelpersPolyfills.cs" Link="Common\Polyfills\RuntimeHelpersPolyfills.cs" />
375+
<Compile Include="$(CommonPath)Polyfills\StopwatchPolyfills.cs" Link="Common\Polyfills\StopwatchPolyfills.cs" />
375376
</ItemGroup>
376377

377378
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp' or $([MSBuild]::VersionLessThan('$(TargetFrameworkVersion)', '9.0'))">

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.Cache.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
#if NETFRAMEWORK || NET
@@ -14,16 +14,16 @@ internal sealed partial class ReflectionEmitCachingMemberAccessor
1414
private sealed class Cache<TKey> where TKey : notnull
1515
{
1616
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.
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.
2020
private readonly ConcurrentDictionary<TKey, CacheEntry> _cache = new();
2121

2222
public Cache(TimeSpan slidingExpiration, TimeSpan evictionInterval)
2323
{
24-
_slidingExpirationTicks = slidingExpiration.Ticks;
25-
_evictionIntervalTicks = evictionInterval.Ticks;
26-
_lastEvictedTicks = DateTime.UtcNow.Ticks;
24+
_slidingExpiration = slidingExpiration;
25+
_evictionInterval = evictionInterval;
26+
_lastEvictedTimestamp = Stopwatch.GetTimestamp();
2727
}
2828

2929
public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where TValue : class?
@@ -36,17 +36,17 @@ public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where
3636
#else
3737
key => new(valueFactory(key)));
3838
#endif
39-
long utcNowTicks = DateTime.UtcNow.Ticks;
40-
Volatile.Write(ref entry.LastUsedTicks, utcNowTicks);
39+
long nowTimestamp = Stopwatch.GetTimestamp();
40+
Volatile.Write(ref entry.LastUsedTimestamp, nowTimestamp);
4141

42-
if (utcNowTicks - Volatile.Read(ref _lastEvictedTicks) >= _evictionIntervalTicks)
42+
if (Stopwatch.GetElapsedTime(Volatile.Read(ref _lastEvictedTimestamp), nowTimestamp) >= _evictionInterval)
4343
{
4444
if (Interlocked.CompareExchange(ref _evictLock, 1, 0) == 0)
4545
{
46-
if (utcNowTicks - _lastEvictedTicks >= _evictionIntervalTicks)
46+
if (Stopwatch.GetElapsedTime(_lastEvictedTimestamp, nowTimestamp) >= _evictionInterval)
4747
{
48-
EvictStaleCacheEntries(utcNowTicks);
49-
Volatile.Write(ref _lastEvictedTicks, utcNowTicks);
48+
EvictStaleCacheEntries(nowTimestamp);
49+
Volatile.Write(ref _lastEvictedTimestamp, nowTimestamp);
5050
}
5151

5252
Volatile.Write(ref _evictLock, 0);
@@ -59,14 +59,14 @@ public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where
5959
public void Clear()
6060
{
6161
_cache.Clear();
62-
_lastEvictedTicks = DateTime.UtcNow.Ticks;
62+
_lastEvictedTimestamp = Stopwatch.GetTimestamp();
6363
}
6464

65-
private void EvictStaleCacheEntries(long utcNowTicks)
65+
private void EvictStaleCacheEntries(long nowTimestamp)
6666
{
6767
foreach (KeyValuePair<TKey, CacheEntry> kvp in _cache)
6868
{
69-
if (utcNowTicks - Volatile.Read(ref kvp.Value.LastUsedTicks) >= _slidingExpirationTicks)
69+
if (Stopwatch.GetElapsedTime(Volatile.Read(ref kvp.Value.LastUsedTimestamp), nowTimestamp) >= _slidingExpiration)
7070
{
7171
_cache.TryRemove(kvp.Key, out _);
7272
}
@@ -76,7 +76,7 @@ private void EvictStaleCacheEntries(long utcNowTicks)
7676
private sealed class CacheEntry
7777
{
7878
public readonly object? Value;
79-
public long LastUsedTicks;
79+
public long LastUsedTimestamp;
8080

8181
public CacheEntry(object? value)
8282
{

0 commit comments

Comments
 (0)