Skip to content

Commit

Permalink
Merge pull request #2 from chabiss/dev/chabiss/vdiag-lsp-refactoring
Browse files Browse the repository at this point in the history
Moving All handling of broker service from EA.D into the implementation IVisualDiagnosticsLanguageService
  • Loading branch information
chabiss authored Mar 18, 2024
2 parents 43555d2 + c9cdb3a commit 28a50e7
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 369 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices;
[Export]
internal class ServiceBrokerFactory
{
private BrokeredServiceContainer? _container;
private readonly TaskCompletionSource<BrokeredServiceContainer> _container;
private readonly ExportProvider _exportProvider;
private Task _bridgeCompletionTask;
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
Expand All @@ -37,6 +37,7 @@ public ServiceBrokerFactory(ExportProvider exportProvider)
{
_exportProvider = exportProvider;
_bridgeCompletionTask = Task.CompletedTask;
_container = new TaskCompletionSource<BrokeredServiceContainer>();
}

/// <summary>
Expand All @@ -48,30 +49,38 @@ public ServiceBrokerFactory(ExportProvider exportProvider)
/// <summary>
/// Returns a full-access service broker, but will return null if we haven't yet connected to the Dev Kit broker.
/// </summary>
public IServiceBroker? TryGetFullAccessServiceBroker() => _container?.GetFullAccessServiceBroker();
public IServiceBroker? TryGetFullAccessServiceBroker() => _container.Task.IsCompletedSuccessfully ? _container.Task.Result?.GetFullAccessServiceBroker() : null;

/// <summary>
/// Returns a full-access service broker container, that consuming code can await on until we connected to the Dev Kit broker
/// </summary>
[Export(typeof(SVsBrokeredServiceContainer))]
public Task<IBrokeredServiceContainer> BrokeredServiceContainerAsync => this.GetRequiredServiceBrokerContainerAsync();

public BrokeredServiceContainer GetRequiredServiceBrokerContainer()
{
Contract.ThrowIfNull(_container);
return _container;
Contract.ThrowIfFalse(_container.Task.IsCompletedSuccessfully);
Contract.ThrowIfNull(_container.Task.Result);
return _container.Task.Result;
}

/// <summary>
/// Creates a service broker instance without connecting via a pipe to another process.
/// </summary>
public async Task CreateAsync()
{
Contract.ThrowIfFalse(_container == null, "We should only create one container.");
Contract.ThrowIfFalse(!_container.Task.IsCompleted, "We should only create one container.");

_container = await BrokeredServiceContainer.CreateAsync(_exportProvider, _cancellationTokenSource.Token);
var container = await BrokeredServiceContainer.CreateAsync(_exportProvider, _cancellationTokenSource.Token);
_container.TrySetResult(container);
}

public async Task CreateAndConnectAsync(string brokeredServicePipeName)
{
await CreateAsync();

var bridgeProvider = _exportProvider.GetExportedValue<BrokeredServiceBridgeProvider>();
_bridgeCompletionTask = bridgeProvider.SetupBrokeredServicesBridgeAsync(brokeredServicePipeName, _container!, _cancellationTokenSource.Token);
_bridgeCompletionTask = bridgeProvider.SetupBrokeredServicesBridgeAsync(brokeredServicePipeName, _container.Task.Result!, _cancellationTokenSource.Token);
}

public Task ShutdownAndWaitForCompletionAsync()
Expand All @@ -82,5 +91,12 @@ public Task ShutdownAndWaitForCompletionAsync()
// completed task set in the constructor, so the waiter no-ops.
return _bridgeCompletionTask;
}

private async Task<IBrokeredServiceContainer> GetRequiredServiceBrokerContainerAsync()
{
// Caller of this method are expected to wait until the container is set by CreateAsync
var brokeredServiceContainer = await _container.Task.ConfigureAwait(false);
return brokeredServiceContainer;
}
}
#pragma warning restore RS0030 // Do not used banned APIs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ internal class Descriptors
{ BrokeredServiceDescriptors.DebuggerManagedHotReloadService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) },
{ BrokeredServiceDescriptors.HotReloadSessionNotificationService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) },
{ BrokeredServiceDescriptors.ManagedHotReloadAgentManagerService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) },
{ BrokeredServiceDescriptors.MauiLaunchCustomizerServiceDescriptor.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) },
}.ToImmutableDictionary();

public static ServiceJsonRpcDescriptor CreateDescriptor(ServiceMoniker serviceMoniker) => new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,10 @@

namespace Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts
{
/// <summary>
/// Process Information
/// </summary>
/// <param name="ProcessId">Unique GUID that uniquely identify a process under a debug session</param>
/// <param name="LocalProcessId">local process running on a host device, if the process running on a different host (like mobile device), this will be null</param>
/// <param name="Path">path to the process, this is guaranteed to not be null for local process, but could be null for mobile devices</param>
/// <param name="ConnectionId">Connection Id identifying a data bridge connection to this process</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0051:Add internal types and members to the declared API", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/7237")]
internal record struct ProcessInfo(Guid ProcessId, uint? LocalProcessId, string? Path, string? ConnectionId);

[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0051:Add internal types and members to the declared API", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/7237")]
internal record struct ConnectionInfo(string? ConnectionId, string? Handle, string? Address, uint? PortNumber);

/// <summary>
/// Workspace service responsible for starting a Visual Diagnostic session on the LSP server
/// </summary>
internal interface IVisualDiagnosticsLanguageService : IWorkspaceService
internal interface IVisualDiagnosticsLanguageService : IWorkspaceService, IDisposable
{
/// <summary>
/// Initialize the diagnostic host
Expand All @@ -35,29 +22,5 @@ internal interface IVisualDiagnosticsLanguageService : IWorkspaceService
/// <param name="token">Cancellation token</param>
/// <returns></returns>
public Task InitializeAsync(IServiceBroker serviceBroker, CancellationToken token);

/// <summary>
/// Request the creation of a data bridge connection info given a connection Id
/// </summary>
/// <param name="connectionId">Unique identifiable token for a given connection</param>
/// <param name="token">Cancellation token</param>
/// <returns>a ConnectionInfo record <seealso cref="ConnectionInfo"/></returns>
public Task<ConnectionInfo> RequestDataBridgeConnectionAsync(string connectionId, CancellationToken token);

/// <summary>
/// Notifies the diagnostic host workspace service that a debugging session has started.
/// </summary>
/// <param name="info">Process information <seealso cref="ProcessInfo"/></param>
/// <param name="token">Cancellation token</param>
/// <returns></returns>
public Task HandleDiagnosticSessionStartAsync(ProcessInfo info, CancellationToken token);

/// <summary>
/// Notifies the diagnostic host workspace service that a debugging session has ended.
/// </summary>
/// <param name="info">Process information <seealso cref="ProcessInfo"/></param>
/// <param name="token">Cancellation token</param>
/// <returns></returns>
public Task HandleDiagnosticSessionStopAsync(ProcessInfo info, CancellationToken token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@

using System.Threading.Tasks;
using Microsoft.ServiceHub.Framework;
using Microsoft.VisualStudio.Debugger.Contracts.HotReload;

namespace Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts
{
/// <summary>
/// Facade interface for getting various service brokers
/// Facade interface for getting broker service successfully through MEF
/// </summary>
internal interface IVisualDiagnosticsBrokeredDebuggerServices
internal interface IVisualDiagnosticsServiceBroker
{
Task<IServiceBroker> GetServiceBrokerAsync();
Task<IHotReloadSessionNotificationService?> GetHotReloadSessionNotificationServiceAsync();
Task<IManagedHotReloadAgentManagerService?> GetManagedHotReloadAgentManagerServiceAsync();
Task<IManagedHotReloadService?> GetManagedHotReloadServiceAsync();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.ServiceHub.Framework;
using Microsoft.VisualStudio.Composition;
using Microsoft.VisualStudio.Shell.ServiceBroker;

namespace Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Internal
{
/// <summary>
/// This is a simple wrapper to succeed at getting the broker service using System.ComponentModel.Composition inside an LSP service OnInitialized factory
/// </summary>
[Export(typeof(IVisualDiagnosticsServiceBroker))]
internal sealed class VisualDiagnosticsServiceBroker : IVisualDiagnosticsServiceBroker
{
private readonly Lazy<Task<IBrokeredServiceContainer>> _container;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public VisualDiagnosticsServiceBroker(
[Import(typeof(SVsBrokeredServiceContainer))]
Lazy<Task<IBrokeredServiceContainer>> serviceBroker)
{
_container = serviceBroker;
}

public async Task<IServiceBroker> GetServiceBrokerAsync()
{
// Waiting on the container to be created
await _container.Value.ConfigureAwait(false);
return _container.Value.Result.GetFullAccessServiceBroker();
}
}
}
Loading

0 comments on commit 28a50e7

Please sign in to comment.