AbstractAnalyzerState.cs 4.6 KB
Newer Older
S
Sam Harwell 已提交
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
2

3
using System;
4 5 6 7 8
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
9
using Microsoft.CodeAnalysis.Shared.Utilities;
10 11 12 13 14 15
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.SolutionCrawler.State
{
    internal abstract class AbstractAnalyzerState<TKey, TValue, TData>
    {
16
        protected readonly ConcurrentDictionary<TKey, CacheEntry> DataCache = new ConcurrentDictionary<TKey, CacheEntry>(concurrencyLevel: 2, capacity: 10);
17 18 19 20

        protected abstract TKey GetCacheKey(TValue value);
        protected abstract Solution GetSolution(TValue value);
        protected abstract bool ShouldCache(TValue value);
21
        protected abstract int GetCount(TData data);
22 23 24 25 26 27 28

        protected abstract Task<Stream> ReadStreamAsync(IPersistentStorage storage, TValue value, CancellationToken cancellationToken);
        protected abstract TData TryGetExistingData(Stream stream, TValue value, CancellationToken cancellationToken);

        protected abstract void WriteTo(Stream stream, TData data, CancellationToken cancellationToken);
        protected abstract Task<bool> WriteStreamAsync(IPersistentStorage storage, TValue value, Stream stream, CancellationToken cancellationToken);

29
        public int Count => this.DataCache.Count;
30

31 32
        public int GetDataCount(TKey key)
        {
C
CyrusNajmabadi 已提交
33
            if (!this.DataCache.TryGetValue(key, out var entry))
34 35 36 37 38 39 40
            {
                return 0;
            }

            return entry.Count;
        }

41 42
        public async Task<TData> TryGetExistingDataAsync(TValue value, CancellationToken cancellationToken)
        {
C
CyrusNajmabadi 已提交
43
            if (!this.DataCache.TryGetValue(GetCacheKey(value), out var entry))
44 45
            {
                // we don't have data
C
CyrusNajmabadi 已提交
46
                return default;
47 48 49
            }

            // we have in memory cache for the document
50
            if (entry.HasCachedData)
51
            {
52
                return entry.Data;
53 54
            }

55
            // we have persisted data
56 57 58
            var solution = GetSolution(value);
            var persistService = solution.Workspace.Services.GetService<IPersistentStorageService>();

59
            try
60
            {
C
Cyrus Najmabadi 已提交
61 62 63 64
                using var storage = persistService.GetStorage(solution);
                using var stream = await ReadStreamAsync(storage, value, cancellationToken).ConfigureAwait(false);

                if (stream != null)
65
                {
C
Cyrus Najmabadi 已提交
66
                    return TryGetExistingData(stream, value, cancellationToken);
67 68
                }
            }
69
            catch (Exception e) when (IOUtilities.IsNormalIOException(e))
70 71 72
            {
            }

C
CyrusNajmabadi 已提交
73
            return default;
74 75 76 77 78 79 80 81 82 83
        }

        public async Task PersistAsync(TValue value, TData data, CancellationToken cancellationToken)
        {
            var succeeded = await WriteToStreamAsync(value, data, cancellationToken).ConfigureAwait(false);

            var id = GetCacheKey(value);

            // if data is for opened document or if persistence failed, 
            // we keep small cache so that we don't pay cost of deserialize/serializing data that keep changing
C
CyrusNajmabadi 已提交
84
            this.DataCache[id] = (!succeeded || ShouldCache(value)) ? new CacheEntry(data, GetCount(data)) : new CacheEntry(default, GetCount(data));
85 86
        }

87
        public virtual bool Remove(TKey id)
88 89 90
        {
            // remove doesn't actually remove data from the persistent storage
            // that will be automatically managed by the service itself.
91
            return this.DataCache.TryRemove(id, out _);
92 93 94 95
        }

        private async Task<bool> WriteToStreamAsync(TValue value, TData data, CancellationToken cancellationToken)
        {
C
Cyrus Najmabadi 已提交
96 97
            using var stream = SerializableBytes.CreateWritableStream();
            WriteTo(stream, data, cancellationToken);
98

C
Cyrus Najmabadi 已提交
99 100
            var solution = GetSolution(value);
            var persistService = solution.Workspace.Services.GetService<IPersistentStorageService>();
101

C
Cyrus Najmabadi 已提交
102 103 104
            using var storage = persistService.GetStorage(solution);
            stream.Position = 0;
            return await WriteStreamAsync(storage, value, stream, cancellationToken).ConfigureAwait(false);
105
        }
106

107
        protected readonly struct CacheEntry
108 109 110 111 112 113 114 115 116 117
        {
            public readonly TData Data;
            public readonly int Count;

            public CacheEntry(TData data, int count)
            {
                Data = data;
                Count = count;
            }

C
CyrusNajmabadi 已提交
118
            public bool HasCachedData => !object.Equals(Data, default);
119
        }
120 121
    }
}