Skip to content

Commit 0b52155

Browse files
BackgroundDocumentGenerator: Use HashSet<DocumentKey> for unique items
Update BackgroundDocumentGenerator to use a dedicated HashSet instance to compute the most recent unique items. This is a larger change that makes BackgroundDocumentGenerator track work by DocumentKey rather than (ProjectSnapshot, DocumentSnapshot): - Update BackgroundDocumentGenerator's async batching work queue to track DocumentKeys rather than (ProjectSnapshot, DocumentSnapshot). - Update document suppression to use DocumentKey.
1 parent 700d939 commit 0b52155

File tree

5 files changed

+60
-98
lines changed

5 files changed

+60
-98
lines changed

src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/DynamicFiles/BackgroundDocumentGenerator.Comparer.cs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/DynamicFiles/BackgroundDocumentGenerator.cs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Licensed under the MIT license. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Collections.Immutable;
67
using System.ComponentModel.Composition;
78
using System.IO;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Microsoft.AspNetCore.Razor;
12+
using Microsoft.AspNetCore.Razor.ProjectSystem;
1113
using Microsoft.AspNetCore.Razor.Utilities;
1214
using Microsoft.CodeAnalysis.Razor;
1315
using Microsoft.CodeAnalysis.Razor.Logging;
@@ -29,7 +31,8 @@ internal partial class BackgroundDocumentGenerator : IRazorStartupService, IDisp
2931
private readonly ILogger _logger;
3032

3133
private readonly CancellationTokenSource _disposeTokenSource;
32-
private readonly AsyncBatchingWorkQueue<(ProjectSnapshot, DocumentSnapshot)> _workQueue;
34+
private readonly AsyncBatchingWorkQueue<DocumentKey> _workQueue;
35+
private readonly HashSet<DocumentKey> _workerSet;
3336
private ImmutableHashSet<string> _suppressedDocuments;
3437
private bool _solutionIsClosing;
3538

@@ -57,8 +60,9 @@ protected BackgroundDocumentGenerator(
5760
_loggerFactory = loggerFactory;
5861
_logger = loggerFactory.GetOrCreateLogger<BackgroundDocumentGenerator>();
5962

63+
_workerSet = [];
6064
_disposeTokenSource = new();
61-
_workQueue = new AsyncBatchingWorkQueue<(ProjectSnapshot, DocumentSnapshot)>(
65+
_workQueue = new AsyncBatchingWorkQueue<DocumentKey>(
6266
delay,
6367
processBatchAsync: ProcessBatchAsync,
6468
equalityComparer: null,
@@ -82,52 +86,59 @@ public void Dispose()
8286
protected Task WaitUntilCurrentBatchCompletesAsync()
8387
=> _workQueue.WaitUntilCurrentBatchCompletesAsync();
8488

85-
protected virtual async Task ProcessDocumentAsync(ProjectSnapshot project, DocumentSnapshot document, CancellationToken cancellationToken)
89+
protected virtual async Task ProcessDocumentAsync(DocumentSnapshot document, CancellationToken cancellationToken)
8690
{
8791
await document.GetGeneratedOutputAsync(cancellationToken).ConfigureAwait(false);
8892

89-
UpdateFileInfo(project, document);
93+
UpdateFileInfo(document);
9094
}
9195

92-
public virtual void Enqueue(ProjectSnapshot project, DocumentSnapshot document)
96+
public virtual void Enqueue(DocumentKey documentKey)
9397
{
9498
if (_disposeTokenSource.IsCancellationRequested)
9599
{
96100
return;
97101
}
98102

99-
if (_fallbackProjectManager.IsFallbackProject(project.Key))
103+
if (_fallbackProjectManager.IsFallbackProject(documentKey.ProjectKey))
100104
{
101105
// We don't support closed file code generation for fallback projects
102106
return;
103107
}
104108

105-
if (Suppressed(project, document))
109+
if (Suppressed(documentKey))
106110
{
107111
return;
108112
}
109113

110-
_workQueue.AddWork((project, document));
114+
_workQueue.AddWork(documentKey);
111115
}
112116

113-
protected virtual async ValueTask ProcessBatchAsync(ImmutableArray<(ProjectSnapshot, DocumentSnapshot)> items, CancellationToken token)
117+
protected virtual async ValueTask ProcessBatchAsync(ImmutableArray<DocumentKey> items, CancellationToken token)
114118
{
115-
foreach (var (project, document) in items.GetMostRecentUniqueItems(Comparer.Instance))
119+
_workerSet.Clear();
120+
121+
foreach (var key in items.GetMostRecentUniqueItems(_workerSet))
116122
{
117123
if (token.IsCancellationRequested)
118124
{
119125
return;
120126
}
121127

122-
// If the solution is closing, suspect any in-progress work
128+
// If the solution is closing, escape any in-progress work
123129
if (_solutionIsClosing)
124130
{
125131
break;
126132
}
127133

134+
if (!_projectManager.TryGetDocument(key, out var document))
135+
{
136+
continue;
137+
}
138+
128139
try
129140
{
130-
await ProcessDocumentAsync(project, document, token).ConfigureAwait(false);
141+
await ProcessDocumentAsync(document, token).ConfigureAwait(false);
131142
}
132143
catch (UnauthorizedAccessException)
133144
{
@@ -140,34 +151,34 @@ protected virtual async ValueTask ProcessBatchAsync(ImmutableArray<(ProjectSnaps
140151
}
141152
catch (Exception ex)
142153
{
143-
_logger.LogError(ex, $"Error encountered from project '{project.FilePath}':{Environment.NewLine}{ex}");
154+
_logger.LogError(ex, $"Error encountered from project '{document.Project.FilePath}':{Environment.NewLine}{ex}");
144155
}
145156
}
146157
}
147158

148-
private bool Suppressed(ProjectSnapshot project, DocumentSnapshot document)
159+
private bool Suppressed(DocumentKey documentKey)
149160
{
150-
var filePath = document.FilePath;
161+
var filePath = documentKey.FilePath;
151162

152163
if (_projectManager.IsDocumentOpen(filePath))
153164
{
154165
ImmutableInterlocked.Update(ref _suppressedDocuments, static (set, filePath) => set.Add(filePath), filePath);
155-
_infoProvider.SuppressDocument(project.Key, filePath);
166+
_infoProvider.SuppressDocument(documentKey);
156167
return true;
157168
}
158169

159170
ImmutableInterlocked.Update(ref _suppressedDocuments, static (set, filePath) => set.Remove(filePath), filePath);
160171
return false;
161172
}
162173

163-
private void UpdateFileInfo(ProjectSnapshot project, DocumentSnapshot document)
174+
private void UpdateFileInfo(DocumentSnapshot document)
164175
{
165176
var filePath = document.FilePath;
166177

167178
if (!_suppressedDocuments.Contains(filePath))
168179
{
169180
var container = new DefaultDynamicDocumentContainer(document, _loggerFactory);
170-
_infoProvider.UpdateFileInfo(project.Key, container);
181+
_infoProvider.UpdateFileInfo(document.Project.Key, container);
171182
}
172183
}
173184

@@ -190,9 +201,9 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
190201

191202
foreach (var documentFilePath in newProject.DocumentFilePaths)
192203
{
193-
if (newProject.TryGetDocument(documentFilePath, out var document))
204+
if (newProject.ContainsDocument(documentFilePath))
194205
{
195-
Enqueue(newProject, document);
206+
Enqueue(new(newProject.Key, documentFilePath));
196207
}
197208
}
198209

@@ -205,9 +216,9 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
205216

206217
foreach (var documentFilePath in newProject.DocumentFilePaths)
207218
{
208-
if (newProject.TryGetDocument(documentFilePath, out var document))
219+
if (newProject.ContainsDocument(documentFilePath))
209220
{
210-
Enqueue(newProject, document);
221+
Enqueue(new(newProject.Key, documentFilePath));
211222
}
212223
}
213224

@@ -222,11 +233,11 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
222233

223234
if (newProject.TryGetDocument(documentFilePath, out var document))
224235
{
225-
Enqueue(newProject, document);
236+
Enqueue(document.Key);
226237

227238
foreach (var relatedDocument in newProject.GetRelatedDocuments(document))
228239
{
229-
Enqueue(newProject, relatedDocument);
240+
Enqueue(relatedDocument.Key);
230241
}
231242
}
232243

@@ -245,7 +256,7 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
245256
{
246257
foreach (var relatedDocument in newProject.GetRelatedDocuments(document))
247258
{
248-
Enqueue(newProject, relatedDocument);
259+
Enqueue(relatedDocument.Key);
249260
}
250261
}
251262

src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/DynamicFiles/IRazorDynamicFileInfoProviderInternal.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ internal interface IRazorDynamicFileInfoProviderInternal
1010
{
1111
void UpdateLSPFileInfo(Uri documentUri, IDynamicDocumentContainer documentContainer);
1212
void UpdateFileInfo(ProjectKey projectKey, IDynamicDocumentContainer documentContainer);
13-
void SuppressDocument(ProjectKey projectKey, string documentFilePath);
13+
void SuppressDocument(DocumentKey documentKey);
1414
}

src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/DynamicFiles/RazorDynamicFileInfoProvider.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,10 @@ private IEnumerable<KeyValuePair<Key, Entry>> GetAllKeysForPath(string filePath)
205205
}
206206

207207
// Called by us when a document opens in the editor
208-
public void SuppressDocument(ProjectKey projectKey, string documentFilePath)
208+
public void SuppressDocument(DocumentKey documentKey)
209209
{
210210
Debug.Assert(!_languageServerFeatureOptions.UseRazorCohostServer, "Should never be called in cohosting");
211211

212-
if (documentFilePath is null)
213-
{
214-
throw new ArgumentNullException(nameof(documentFilePath));
215-
}
216-
217212
if (_lspEditorFeatureDetector.IsLspEditorEnabled())
218213
{
219214
return;
@@ -222,13 +217,13 @@ public void SuppressDocument(ProjectKey projectKey, string documentFilePath)
222217
// There's a possible race condition here where we're processing an update
223218
// and the project is getting unloaded. So if we don't find an entry we can
224219
// just ignore it.
225-
var projectId = TryFindProjectIdForProjectKey(projectKey);
220+
var projectId = TryFindProjectIdForProjectKey(documentKey.ProjectKey);
226221
if (projectId is null)
227222
{
228223
return;
229224
}
230225

231-
var key = new Key(projectId, documentFilePath);
226+
var key = new Key(projectId, documentKey.FilePath);
232227
if (_entries.TryGetValue(key, out var entry))
233228
{
234229
var updated = false;
@@ -243,7 +238,7 @@ public void SuppressDocument(ProjectKey projectKey, string documentFilePath)
243238

244239
if (updated)
245240
{
246-
Updated?.Invoke(this, documentFilePath);
241+
Updated?.Invoke(this, documentKey.FilePath);
247242
}
248243
}
249244
}

0 commit comments

Comments
 (0)