Merge pull request #7010 from ryanbrandenburg/CLaSPReaction

Fix shutdown issues from CLaSP
This commit is contained in:
Ryan Brandenburg 2022-10-21 14:13:47 -07:00 коммит произвёл GitHub
Родитель 8e74efe466 78095bfd72
Коммит 937e26d8a8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 49 добавлений и 59 удалений

Просмотреть файл

@ -81,7 +81,7 @@
<RoslynPackageVersion>4.4.0-2.22424.2</RoslynPackageVersion>
<VisualStudioLanguageServerProtocolVersion>17.4.1008-preview</VisualStudioLanguageServerProtocolVersion>
<MicrosoftNetCompilersToolsetVersion>4.4.0-1.final</MicrosoftNetCompilersToolsetVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.5.0-1.22513.3</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.5.0-1.22519.14</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
</PropertyGroup>
<PropertyGroup Label="Manual">
<!-- dotnet/runtime packages -->

Просмотреть файл

@ -130,13 +130,14 @@ namespace Microsoft.AspNetCore.Razor.Microbenchmarks.LanguageServer
}
[GlobalCleanup]
public Task CleanupServerAsync()
public async Task CleanupServerAsync()
{
File.Delete(_filePath);
RazorLanguageServer.Dispose();
var innerServer = RazorLanguageServer.GetInnerLanguageServerForTesting();
return Task.CompletedTask;
await innerServer.ShutdownAsync();
await innerServer.ExitAsync();
}
private void EnsureServicesInitialized()

Просмотреть файл

@ -101,10 +101,11 @@ namespace Microsoft.AspNetCore.Razor.Microbenchmarks.LanguageServer
}
[GlobalCleanup]
public Task CleanupServerAsync()
public async Task CleanupServerAsync()
{
RazorLanguageServer.Dispose();
return Task.CompletedTask;
var innerServer = RazorLanguageServer.GetInnerLanguageServerForTesting();
await innerServer.ShutdownAsync();
await innerServer.ExitAsync();
}
protected internal override void Builder(IServiceCollection collection)

Просмотреть файл

@ -113,11 +113,12 @@ namespace Microsoft.AspNetCore.Razor.Microbenchmarks.LanguageServer
}
[GlobalCleanup]
public Task CleanupServerAsync()
public async Task CleanupServerAsync()
{
RazorLanguageServer.Dispose();
var innerServer = RazorLanguageServer.GetInnerLanguageServerForTesting();
return Task.CompletedTask;
await innerServer.ShutdownAsync();
await innerServer.ExitAsync();
}
protected internal override void Builder(IServiceCollection collection)

Просмотреть файл

@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Common
_filePath = filePath;
}
public override Task<TextAndVersion> LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
public override Task<TextAndVersion> LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken)
{
TextAndVersion textAndVersion;

Просмотреть файл

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
_documentSnapshot = documentSnapshot;
}
public override async Task<TextAndVersion> LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken)
public override async Task<TextAndVersion> LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken)
{
var sourceText = await _documentSnapshot.GetTextAsync();
var textAndVersion = TextAndVersion.Create(sourceText, VersionStamp.Default);

Просмотреть файл

@ -32,7 +32,6 @@ internal static class IServiceCollectionExtensions
{
services.AddHandler<RazorInitializeEndpoint>();
services.AddHandler<RazorInitializedEndpoint>();
services.AddHandler<ShutdownHandler<RazorRequestContext>>();
var razorLifeCycleManager = new RazorLifeCycleManager(razorLanguageServer);
services.AddSingleton<ILifeCycleManager>(razorLifeCycleManager);

Просмотреть файл

@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer;
internal class LspServices : ILspServices
{
private readonly IServiceProvider _serviceProvider;
public bool IsDisposed = false;
public LspServices(IServiceCollection serviceCollection)
{
@ -57,6 +58,7 @@ internal class LspServices : ILspServices
if (_serviceProvider is IDisposable disposable)
{
disposable.Dispose();
IsDisposed = true;
}
}
}

Просмотреть файл

@ -497,8 +497,8 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
_fromDocument = fromDocument ?? throw new ArgumentNullException(nameof(fromDocument));
}
public override async Task<TextAndVersion> LoadTextAndVersionAsync(
Workspace workspace,
DocumentId documentId,
Workspace? workspace,
DocumentId? documentId,
CancellationToken cancellationToken)
{
var sourceText = await _fromDocument.GetTextAsync();

Просмотреть файл

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Common.Extensions;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Serialization;
@ -68,10 +69,25 @@ internal sealed class RazorLanguageServerWrapper : IDisposable
public Task WaitForExitAsync()
{
var lifeCycleManager = GetRequiredService<RazorLifeCycleManager>();
var lspServices = _innerServer.GetLspServices();
if (lspServices is LspServices razorServices)
{
// If the LSP Server is already disposed it means the server has already exited.
if (razorServices.IsDisposed)
{
return Task.CompletedTask;
}
else
{
var lifeCycleManager = razorServices.GetRequiredService<RazorLifeCycleManager>();
return lifeCycleManager.WaitForExit;
}
}
else
{
throw new NotImplementedException($"LspServices should always be of type {nameof(LspServices)}.");
}
}
internal T GetRequiredService<T>() where T : notnull
{

Просмотреть файл

@ -16,17 +16,18 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
_languageServer = languageServer;
}
public async Task ExitAsync()
public Task ExitAsync()
{
await _languageServer.ExitAsync();
var services = _languageServer.GetLspServices();
services.Dispose();
_tcs.TrySetResult(0);
return Task.CompletedTask;
}
public async Task ShutdownAsync(string message = "Shutting down")
public Task ShutdownAsync(string message = "Shutting down")
{
await _languageServer.ShutdownAsync(message);
return Task.CompletedTask;
}
public Task WaitForExit => _tcs.Task;

Просмотреть файл

@ -158,7 +158,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
_filePath = filePath;
}
public override Task<TextAndVersion> LoadTextAndVersionAsync(CodeAnalysisWorkspace workspace, DocumentId documentId, CancellationToken cancellationToken)
public override Task<TextAndVersion> LoadTextAndVersionAsync(CodeAnalysisWorkspace? workspace, DocumentId? documentId, CancellationToken cancellationToken)
{
return Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Default, _filePath));
}

Просмотреть файл

@ -40,9 +40,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
private readonly RazorLanguageServerLogHubLoggerProviderFactory _logHubLoggerProviderFactory;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
private readonly VisualStudioHostServicesProvider? _vsHostWorkspaceServicesProvider;
private readonly object _shutdownLock;
private RazorLanguageServerWrapper? _server;
private IDisposable? _serverShutdownDisposable;
private LogHubLoggerProvider? _loggerProvider;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
@ -108,7 +106,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
_logHubLoggerProviderFactory = logHubLoggerProviderFactory;
_languageServerFeatureOptions = languageServerFeatureOptions;
_vsHostWorkspaceServicesProvider = vsHostWorkspaceServicesProvider;
_shutdownLock = new object();
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
}
@ -138,7 +135,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
var (clientStream, serverStream) = FullDuplexStream.CreatePair();
await EnsureCleanedUpServerAsync(token).ConfigureAwait(false);
await EnsureCleanedUpServerAsync().ConfigureAwait(false);
var traceLevel = GetVerbosity();
@ -176,46 +173,19 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
return result;
}
private async Task EnsureCleanedUpServerAsync(CancellationToken token)
private async Task EnsureCleanedUpServerAsync()
{
const int WaitForShutdownAttempts = 10;
if (_server is null)
{
// Server was already cleaned up
return;
}
var attempts = 0;
while (_server is not null && ++attempts < WaitForShutdownAttempts)
{
// Server failed to shutdown, lets wait a little bit and check again.
await Task.Delay(100, token).ConfigureAwait(false);
}
if (_server is not null)
{
// Server still hasn't shutdown, attempt an ungraceful shutdown.
_server.Dispose();
ServerShutdown();
}
}
private void ServerShutdown()
{
lock (_shutdownLock)
{
if (_server is null)
{
// Already shutdown
return;
}
_projectConfigurationFilePathStore.Changed -= ProjectConfigurationFilePathStore_Changed;
_serverShutdownDisposable?.Dispose();
_serverShutdownDisposable = null;
_server = null;
// Server still hasn't shutdown, wait for it to shutdown
await _server.WaitForExitAsync().ConfigureAwait(false);
}
}

Просмотреть файл

@ -36,8 +36,7 @@ public class RazorLanguageServerTest : TestBase
// We turn this into a Set to handle cases like Completion where we have two handlers, only one of which will be registered
// CLaSP will throw if two handlers register for the same method, so if THAT doesn't hold it's a CLaSP bug, not a Razor bug.
var typeMethods = handlerTypes.Select(t => GetMethodFromType(t)).ToHashSet();
// The shutdown handler is outside of our assembly.
typeMethods.Add("shutdown");
if (registeredMethods.Length != typeMethods.Count)
{
var unregisteredHandlers = typeMethods.Where(t => !registeredMethods.Any(m => m.MethodName == t));