Skip to content

Commit

Permalink
Merge branch 'release/vs16.0-preview4'
Browse files Browse the repository at this point in the history
  • Loading branch information
NTaylorMullen committed Feb 1, 2019
2 parents 77532db + a562164 commit 5af7556
Show file tree
Hide file tree
Showing 55 changed files with 1,260 additions and 1,594 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class BackgroundCodeGenerationBenchmark : ProjectSnapshotManagerBenchmark
public void Setup()
{
SnapshotManager = CreateProjectSnapshotManager();
SnapshotManager.HostProjectAdded(HostProject);
SnapshotManager.ProjectAdded(HostProject);
SnapshotManager.Changed += SnapshotManager_Changed;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void Setup()
[Benchmark(Description = "Initializes a project and 100 files", OperationsPerInvoke = 100)]
public void ProjectLoad_AddProjectAnd100Files()
{
SnapshotManager.HostProjectAdded(HostProject);
SnapshotManager.ProjectAdded(HostProject);

for (var i= 0; i < Documents.Length; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public StaticTagHelperResolver(IReadOnlyList<TagHelperDescriptor> tagHelpers)
this._tagHelpers = tagHelpers;
}

public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, ProjectSnapshot projectSnapshot, CancellationToken cancellationToken = default)
{
return Task.FromResult(new TagHelperResolutionResult(_tagHelpers, Array.Empty<RazorDiagnostic>()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;

namespace Microsoft.CodeAnalysis.Razor
{
[Shared]
[Export(typeof(ProjectWorkspaceStateGenerator))]
[Export(typeof(ProjectSnapshotChangeTrigger))]
internal class DefaultProjectWorkspaceStateGenerator : ProjectWorkspaceStateGenerator, IDisposable
{
// Internal for testing
internal readonly Dictionary<string, UpdateItem> _updates;

private readonly ForegroundDispatcher _foregroundDispatcher;
private ProjectSnapshotManagerBase _projectManager;
private TagHelperResolver _tagHelperResolver;

[ImportingConstructor]
public DefaultProjectWorkspaceStateGenerator(ForegroundDispatcher foregroundDispatcher)
{
if (foregroundDispatcher == null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}

_foregroundDispatcher = foregroundDispatcher;

_updates = new Dictionary<string, UpdateItem>(FilePathComparer.Instance);
}

// Used in unit tests to ensure we can control when background work starts.
public ManualResetEventSlim BlockBackgroundWorkStart { get; set; }

// Used in unit tests to ensure we can know when background work finishes.
public ManualResetEventSlim NotifyBackgroundWorkCompleted { get; set; }

public override void Initialize(ProjectSnapshotManagerBase projectManager)
{
if (projectManager == null)
{
throw new ArgumentNullException(nameof(projectManager));
}

_projectManager = projectManager;

var razorLanguageServices = _projectManager.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
_tagHelperResolver = razorLanguageServices.GetRequiredService<TagHelperResolver>();
}

public override void Update(Project workspaceProject, ProjectSnapshot projectSnapshot)
{
if (projectSnapshot == null)
{
throw new ArgumentNullException(nameof(projectSnapshot));
}

_foregroundDispatcher.AssertForegroundThread();

if (_updates.TryGetValue(projectSnapshot.FilePath, out var updateItem) &&
!updateItem.Task.IsCompleted)
{
updateItem.Cts.Cancel();
}

updateItem?.Cts.Dispose();

var cts = new CancellationTokenSource();
var updateTask = Task.Factory.StartNew(
() => UpdateWorkspaceStateAsync(workspaceProject, projectSnapshot, cts.Token),
cts.Token,
TaskCreationOptions.None,
_foregroundDispatcher.BackgroundScheduler).Unwrap();
updateTask.ConfigureAwait(false);
updateItem = new UpdateItem(updateTask, cts);
_updates[projectSnapshot.FilePath] = updateItem;
}

public void Dispose()
{
_foregroundDispatcher.AssertForegroundThread();

foreach (var update in _updates)
{
if (!update.Value.Task.IsCompleted)
{
update.Value.Cts.Cancel();
}
}

BlockBackgroundWorkStart?.Set();
}

private async Task UpdateWorkspaceStateAsync(Project workspaceProject, ProjectSnapshot projectSnapshot, CancellationToken cancellationToken)
{
try
{
_foregroundDispatcher.AssertBackgroundThread();

OnStartingBackgroundWork();

if (cancellationToken.IsCancellationRequested)
{
// Silently cancel, we're the only ones creating these tasks.
return;
}

var workspaceState = ProjectWorkspaceState.Default;
try
{
if (workspaceProject != null)
{
var tagHelperResolutionResult = await _tagHelperResolver.GetTagHelpersAsync(workspaceProject, projectSnapshot, cancellationToken);
workspaceState = new ProjectWorkspaceState(tagHelperResolutionResult.Descriptors);
}
}
catch (Exception ex)
{
await Task.Factory.StartNew(
() => _projectManager.ReportError(ex, projectSnapshot),
CancellationToken.None, // Don't allow errors to be cancelled
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);
return;
}

if (cancellationToken.IsCancellationRequested)
{
// Silently cancel, we're the only ones creating these tasks.
return;
}

await Task.Factory.StartNew(
() =>
{
if (cancellationToken.IsCancellationRequested)
{
return;
}

ReportWorkspaceStateChange(projectSnapshot.FilePath, workspaceState);
},
cancellationToken,
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);
}
catch (Exception ex)
{
// This is something totally unexpected, let's just send it over to the project manager.
await Task.Factory.StartNew(
() => _projectManager.ReportError(ex),
CancellationToken.None, // Don't allow errors to be cancelled
TaskCreationOptions.None,
_foregroundDispatcher.ForegroundScheduler).ConfigureAwait(false);
}

OnBackgroundWorkCompleted();
}

private void ReportWorkspaceStateChange(string projectFilePath, ProjectWorkspaceState workspaceStateChange)
{
_foregroundDispatcher.AssertForegroundThread();

_projectManager.ProjectWorkspaceStateChanged(projectFilePath, workspaceStateChange);
}

private void OnStartingBackgroundWork()
{
if (BlockBackgroundWorkStart != null)
{
BlockBackgroundWorkStart.Wait();
BlockBackgroundWorkStart.Reset();
}
}

private void OnBackgroundWorkCompleted()
{
if (NotifyBackgroundWorkCompleted != null)
{
NotifyBackgroundWorkCompleted.Set();
}
}

// Internal for testing
internal class UpdateItem
{
public UpdateItem(Task task, CancellationTokenSource cts)
{
if (task == null)
{
throw new ArgumentNullException(nameof(task));
}

if (cts == null)
{
throw new ArgumentNullException(nameof(cts));
}

Task = task;
Cts = cts;
}

public Task Task { get; }

public CancellationTokenSource Cts { get; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ public DefaultProjectSnapshot(ProjectState state)

public HostProject HostProject => State.HostProject;

public override bool IsInitialized => WorkspaceProject != null;

public override VersionStamp Version => State.Version;

public override Project WorkspaceProject => State.WorkspaceProject;
public override IReadOnlyList<TagHelperDescriptor> TagHelpers => State.TagHelpers;

#pragma warning disable CS0672 // Member overrides obsolete member
public override bool IsInitialized => throw new NotImplementedException();

public override Project WorkspaceProject => throw new NotImplementedException();
#pragma warning restore CS0672 // Member overrides obsolete member

public override DocumentSnapshot GetDocument(string filePath)
{
Expand Down Expand Up @@ -92,22 +96,17 @@ public override RazorProjectEngine GetProjectEngine()
return State.ProjectEngine;
}

#pragma warning disable CS0672 // Member overrides obsolete member
public override Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync()
{
// IMPORTANT: Don't put more code here. We want this to return a cached task.
return State.GetTagHelpersAsync(this);
return Task.FromResult(TagHelpers);
}

public override bool TryGetTagHelpers(out IReadOnlyList<TagHelperDescriptor> result)
{
if (State.IsTagHelperResultAvailable)
{
result = State.GetTagHelpersAsync(this).Result;
return true;
}

result = null;
return false;
result = TagHelpers;
return true;
}
#pragma warning restore CS0672 // Member overrides obsolete member
}
}
Loading

0 comments on commit 5af7556

Please sign in to comment.