RecoverableTextAndVersion.cs 6.2 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
        public ITemporaryTextStorage Storage => _text?.Storage;

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
        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 已提交
53 54
        public bool TryGetTextVersion(out VersionStamp version)
        {
M
Matt Warren 已提交
55
            version = _version;
56 57 58 59 60 61 62 63 64 65 66 67

            // 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 已提交
68 69 70
            return version != default(VersionStamp);
        }

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

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

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

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

104
        private TextAndVersion InitRecoverable(TextAndVersion textAndVersion)
P
Pilchie 已提交
105
        {
106 107 108 109 110 111 112 113
            _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 已提交
114
        private sealed class RecoverableText : RecoverableWeakValueSource<SourceText>
115 116 117 118 119 120 121
        {
            private readonly RecoverableTextAndVersion _parent;
            private ITemporaryTextStorage _storage;

            public RecoverableText(RecoverableTextAndVersion parent, SourceText text)
                : base(new ConstantValueSource<SourceText>(text))
            {
122 123 124 125
                // TODO: refactor recoverable text like recoverable tree so that
                //       we can have tree/node concept in recoverable text as well.
                //       basically tree is handle that can live in memory and node is
                //       data that come and go.
126 127 128
                _parent = parent;
            }

129 130
            public ITemporaryTextStorage Storage => _storage;

131 132 133 134 135 136
            protected override async Task<SourceText> RecoverAsync(CancellationToken cancellationToken)
            {
                Contract.ThrowIfNull(_storage);

                using (Logger.LogBlock(FunctionId.Workspace_Recoverable_RecoverTextAsync, _parent._filePath, cancellationToken))
                {
137
                    return await _storage.ReadTextAsync(cancellationToken).ConfigureAwait(false);
138 139 140 141 142
                }
            }

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

145 146
                using (Logger.LogBlock(FunctionId.Workspace_Recoverable_RecoverText, _parent._filePath, cancellationToken))
                {
147
                    return _storage.ReadText(cancellationToken);
148 149 150
                }
            }

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

155 156 157 158 159
                var storage = _parent._storageService.CreateTemporaryTextStorage(cancellationToken);
                await storage.WriteTextAsync(text).ConfigureAwait(false);

                // make sure write is done before setting _storage field
                Interlocked.CompareExchange(ref _storage, storage, null);
160
            }
P
Pilchie 已提交
161 162
        }
    }
163
}