-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
MetadataCache.cs
84 lines (69 loc) · 2.89 KB
/
MetadataCache.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis;
namespace Microsoft.NET.Sdk.Razor.Tool
{
internal class MetadataCache
{
// Store 1000 entries -- arbitrary number
private const int CacheSize = 1000;
private readonly ConcurrentLruCache<string, MetadataCacheEntry> _metadataCache =
new ConcurrentLruCache<string, MetadataCacheEntry>(CacheSize, StringComparer.OrdinalIgnoreCase);
// For testing purposes only.
internal ConcurrentLruCache<string, MetadataCacheEntry> Cache => _metadataCache;
internal Metadata GetMetadata(string fullPath)
{
var timestamp = GetFileTimeStamp(fullPath);
// Check if we have an entry in the dictionary.
if (_metadataCache.TryGetValue(fullPath, out var entry))
{
if (timestamp.HasValue && timestamp.Value == entry.Timestamp)
{
// The file has not changed since we cached it. Return the cached entry.
return entry.Metadata;
}
else
{
// The file has changed recently. Remove the cache entry.
_metadataCache.Remove(fullPath);
}
}
Metadata metadata;
using (var fileStream = File.OpenRead(fullPath))
{
metadata = AssemblyMetadata.CreateFromStream(fileStream, PEStreamOptions.PrefetchMetadata);
}
_metadataCache.GetOrAdd(fullPath, new MetadataCacheEntry(timestamp.Value, metadata));
return metadata;
}
private static DateTime? GetFileTimeStamp(string fullPath)
{
try
{
Debug.Assert(Path.IsPathRooted(fullPath));
return File.GetLastWriteTimeUtc(fullPath);
}
catch (Exception e)
{
// There are several exceptions that can occur here: NotSupportedException or PathTooLongException
// for a bad path, UnauthorizedAccessException for access denied, etc. Rather than listing them all,
// just catch all exceptions and log.
ServerLogger.LogException(e, $"Error getting timestamp of file {fullPath}.");
return null;
}
}
internal struct MetadataCacheEntry
{
public MetadataCacheEntry(DateTime timestamp, Metadata metadata)
{
Debug.Assert(timestamp.Kind == DateTimeKind.Utc);
Timestamp = timestamp;
Metadata = metadata;
}
public DateTime Timestamp { get; }
public Metadata Metadata { get; }
}
}
}