Repository: dotnet/roslyn
File: src/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs (C#)

Code before:
            Capabilities = serverCapabilities,
        };
    }

    static async Task WaitForClientProcessExitAsync(int clientProcessId)
    {
        try
        {
            var clientProcessExitTask = new TaskCompletionSource<bool>();

            using var clientProcess = Process.GetProcessById(clientProcessId);
            clientProcess.EnableRaisingEvents = true;
            clientProcess.Exited += (sender, args) => clientProcessExitTask.SetResult(true);

            if (!clientProcess.HasExited)
            {
                // Wait for the client process to exit.
                await clientProcessExitTask.Task.ConfigureAwait(false);
            }
        }
        catch (ArgumentException)
        {
            // The process has already exited or was never running.
        }

        Process.GetCurrentProcess().Kill();
    }
}


Review comment (suggestion):
Have to be careful doing this in the shared library layer.  For example this will run in VS devenv.  I assume in that case the parent process would be devenv (the same as the current process) and therefore this wouldn't ever run.  

But potentially we should do an upfront check to see if the child process is our process and avoid doing any waiting all together

Code after:
﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler;

[Method(Methods.InitializeName)]
internal sealed class InitializeHandler() : ILspServiceRequestHandler<InitializeParams, InitializeResult>
{
    public bool MutatesSolutionState => true;
    public bool RequiresLSPSolution => false;

    public async Task<InitializeResult> HandleRequestAsync(InitializeParams request, RequestContext context, CancellationToken cancellationToken)
    {
        var clientCapabilitiesManager = context.GetRequiredLspService<IInitializeManager>();
        var clientCapabilities = request.Capabilities;
        clientCapabilitiesManager.SetInitializeParams(request);

        if (request.ProcessId is int clientProcessId && RoslynLanguageServer.TryRegisterClientProcessId(clientProcessId))
            context.Logger.LogInformation("Monitoring client process {clientProcessId} for exit", clientProcessId);

        var capabilitiesProvider = context.GetRequiredLspService<ICapabilitiesProvider>();
        var serverCapabilities = capabilitiesProvider.GetCapabilities(clientCapabilities);

        // Record a telemetry event indicating what capabilities are being provided by the server.
        // Useful for figuring out if a particular session is opted into an LSP feature.
        Logger.Log(FunctionId.LSP_Initialize, KeyValueLogMessage.Create(m =>
        {
            m["serverKind"] = context.ServerKind.ToTelemetryString();
            m["capabilities"] = JsonSerializer.Serialize(serverCapabilities, ProtocolConversions.LspJsonSerializerOptions);
        }));

        return new InitializeResult
        {
            Capabilities = serverCapabilities,
        };
    }
}