RecoverableTextAndVersion.cs 5.6 KB
Newer Older
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.
P
Pilchie 已提交
2 3 4 5 6

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
7
using Microsoft.CodeAnalysis.Text;
P
Pilchie 已提交
8 9 10 11 12
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
    /// <summary>
13
    /// A recoverable TextAndVersion source that saves its text to temporary storage.
P
Pilchie 已提交
14
    /// </summary>
15
    internal class RecoverableTextAndVersion : ValueSource<TextAndVersion>, ITextVersionable
P
Pilchie 已提交
16
    {
17
        private readonly ITemporaryStorageService _storageService;
P
Pilchie 已提交
18

P
Paul Harrington 已提交
19
        private SemaphoreSlim _gateDoNotAccessDirectly; // Lazily created. Access via the Gate property
20 21 22 23 24
        private ValueSource<TextAndVersion> _initialSource;

        private RecoverableText _text;
        private VersionStamp _version;
        private string _filePath;
P
Pilchie 已提交
25 26 27

        public RecoverableTextAndVersion(
            ValueSource<TextAndVersion> initialTextAndVersion,
28
            ITemporaryStorageService storageService)
P
Pilchie 已提交
29
        {
30
            _initialSource = initialTextAndVersion;
31
            _storageService = storageService;
P
Pilchie 已提交
32 33
        }

34
        private SemaphoreSlim Gate => LazyInitialization.EnsureInitialized(ref _gateDoNotAccessDirectly, SemaphoreSlimFactory.Instance);
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

        public override bool TryGetValue(out TextAndVersion value)
        {
            SourceText text;
            if (_text != null && _text.TryGetValue(out text))
            {
                value = TextAndVersion.Create(text, _version, _filePath);
                return true;
            }
            else
            {
                value = null;
                return false;
            }
        }

P
Pilchie 已提交
51 52
        public bool TryGetTextVersion(out VersionStamp version)
        {
M
Matt Warren 已提交
53
            version = _version;
54 55 56 57 58 59 60 61 62 63 64 65

            // if the TextAndVersion has not been stored yet, but it has been observed
            // then try to get version from cached value.
            if (version == default(VersionStamp))
            {
                TextAndVersion textAndVersion;
                if (this.TryGetValue(out textAndVersion))
                {
                    version = textAndVersion.Version;
                }
            }

P
Pilchie 已提交
66 67 68
            return version != default(VersionStamp);
        }

69
        public override TextAndVersion GetValue(CancellationToken cancellationToken = default(CancellationToken))
P
Pilchie 已提交
70
        {
71
            if (_text == null)
P
Pilchie 已提交
72
            {
73 74 75 76 77 78 79
                using (Gate.DisposableWait(cancellationToken))
                {
                    if (_text == null)
                    {
                        return InitRecoverable(_initialSource.GetValue(cancellationToken));
                    }
                }
P
Pilchie 已提交
80
            }
81 82

            return TextAndVersion.Create(_text.GetValue(cancellationToken), _version, _filePath);
P
Pilchie 已提交
83 84
        }

85
        public override async Task<TextAndVersion> GetValueAsync(CancellationToken cancellationToken = default(CancellationToken))
P
Pilchie 已提交
86
        {
87
            if (_text == null)
P
Pilchie 已提交
88
            {
M
Matt Warren 已提交
89
                using (await Gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
90 91 92 93 94 95
                {
                    if (_text == null)
                    {
                        return InitRecoverable(await _initialSource.GetValueAsync(cancellationToken).ConfigureAwait(false));
                    }
                }
P
Pilchie 已提交
96
            }
97 98 99

            var text = await _text.GetValueAsync(cancellationToken).ConfigureAwait(false);
            return TextAndVersion.Create(text, _version, _filePath);
P
Pilchie 已提交
100 101
        }

102
        private TextAndVersion InitRecoverable(TextAndVersion textAndVersion)
P
Pilchie 已提交
103
        {
104 105 106 107 108 109 110 111
            _initialSource = null;
            _version = textAndVersion.Version;
            _filePath = textAndVersion.FilePath;
            _text = new RecoverableText(this, textAndVersion.Text);
            _text.GetValue(CancellationToken.None); // force access to trigger save
            return textAndVersion;
        }

P
Paul Harrington 已提交
112
        private sealed class RecoverableText : RecoverableWeakValueSource<SourceText>
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
        {
            private readonly RecoverableTextAndVersion _parent;
            private ITemporaryTextStorage _storage;

            public RecoverableText(RecoverableTextAndVersion parent, SourceText text)
                : base(new ConstantValueSource<SourceText>(text))
            {
                _parent = parent;
            }

            protected override async Task<SourceText> RecoverAsync(CancellationToken cancellationToken)
            {
                Contract.ThrowIfNull(_storage);

                using (Logger.LogBlock(FunctionId.Workspace_Recoverable_RecoverTextAsync, _parent._filePath, cancellationToken))
                {
129
                    return await _storage.ReadTextAsync(cancellationToken).ConfigureAwait(false);
130 131 132 133 134
                }
            }

            protected override SourceText Recover(CancellationToken cancellationToken)
            {
P
Paul Harrington 已提交
135 136
                Contract.ThrowIfNull(_storage);

137 138
                using (Logger.LogBlock(FunctionId.Workspace_Recoverable_RecoverText, _parent._filePath, cancellationToken))
                {
139
                    return _storage.ReadText(cancellationToken);
140 141 142 143 144
                }
            }

            protected override Task SaveAsync(SourceText text, CancellationToken cancellationToken)
            {
P
Paul Harrington 已提交
145 146
                Contract.ThrowIfFalse(_storage == null); // Cannot save more than once

147 148 149
                _storage = _parent._storageService.CreateTemporaryTextStorage(CancellationToken.None);
                return _storage.WriteTextAsync(text);
            }
P
Pilchie 已提交
150 151
        }
    }
152
}