Skip to content

Commit b31ff2a

Browse files
Move to immutable dictionary (#77981)
2 parents c2a424b + 2185e39 commit b31ff2a

File tree

9 files changed

+300
-303
lines changed

9 files changed

+300
-303
lines changed

src/Features/Core/Portable/Extensions/ExtensionMessageHandlerService.cs

Lines changed: 231 additions & 256 deletions
Large diffs are not rendered by default.

src/Features/Core/Portable/Extensions/IExtensionMessageHandlerFactory.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Collections.Immutable;
66
using System.Reflection;
7+
using System.Threading;
78

89
namespace Microsoft.CodeAnalysis.Extensions;
910

@@ -19,7 +20,8 @@ internal interface IExtensionMessageHandlerFactory
1920
/// <param name="assembly">The assembly to scan for handlers.</param>
2021
/// <param name="extensionIdentifier">Unique identifier of the extension owning this handler.</param>
2122
/// <returns>The handlers.</returns>
22-
ImmutableArray<IExtensionMessageHandlerWrapper<Solution>> CreateWorkspaceMessageHandlers(Assembly assembly, string extensionIdentifier);
23+
ImmutableArray<IExtensionMessageHandlerWrapper<Solution>> CreateWorkspaceMessageHandlers(
24+
Assembly assembly, string extensionIdentifier, CancellationToken cancellationToken);
2325

2426
/// <summary>
2527
/// Creates <see cref="IExtensionMessageHandlerWrapper{Document}"/> instances for each
@@ -28,5 +30,6 @@ internal interface IExtensionMessageHandlerFactory
2830
/// <param name="assembly">The assembly to scan for handlers.</param>
2931
/// <param name="extensionIdentifier">Unique identifier of the extension owning this handler.</param>
3032
/// <returns>The handlers.</returns>
31-
ImmutableArray<IExtensionMessageHandlerWrapper<Document>> CreateDocumentMessageHandlers(Assembly assembly, string extensionIdentifier);
33+
ImmutableArray<IExtensionMessageHandlerWrapper<Document>> CreateDocumentMessageHandlers(
34+
Assembly assembly, string extensionIdentifier, CancellationToken cancellationToken);
3235
}

src/Features/Core/Portable/Extensions/IExtensionMessageHandlerService.cs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,38 @@ namespace Microsoft.CodeAnalysis.Extensions;
1313
/// </summary>
1414
internal interface IExtensionMessageHandlerService : IWorkspaceService
1515
{
16+
/// <summary>
17+
/// Registers extension message handlers from the specified assembly.
18+
/// </summary>
19+
/// <param name="assemblyFilePath">The assembly to register and create message handlers from.</param>
20+
/// <returns>The names of the registered handlers.</returns>
21+
/// <remarks>Should be called serially with other <see cref="RegisterExtensionAsync"/>, <see
22+
/// cref="UnregisterExtensionAsync"/>, or <see cref="ResetAsync"/> calls.</remarks>
23+
ValueTask<RegisterExtensionResponse> RegisterExtensionAsync(string assemblyFilePath, CancellationToken cancellationToken);
24+
25+
/// <summary>
26+
/// Unregisters extension message handlers previously registered from <paramref name="assemblyFilePath"/>.
27+
/// </summary>
28+
/// <param name="assemblyFilePath">The assembly for which handlers should be unregistered.</param>
29+
/// <remarks>Should be called serially with other <see cref="RegisterExtensionAsync"/>, <see
30+
/// cref="UnregisterExtensionAsync"/>, or <see cref="ResetAsync"/> calls.</remarks>
31+
ValueTask UnregisterExtensionAsync(string assemblyFilePath, CancellationToken cancellationToken);
32+
33+
/// <summary>
34+
/// Unregisters all extension message handlers.
35+
/// </summary>
36+
/// <remarks>Should be called serially with other <see cref="RegisterExtensionAsync"/>, <see
37+
/// cref="UnregisterExtensionAsync"/>, or <see cref="ResetAsync"/> calls.</remarks>
38+
ValueTask ResetAsync(CancellationToken cancellationToken);
39+
1640
/// <summary>
1741
/// Executes a non-document-specific extension message handler with the given message and solution.
1842
/// </summary>
1943
/// <param name="solution">The solution the message refers to.</param>
2044
/// <param name="messageName">The name of the handler to execute. This is generally the full name of the type implementing the handler.</param>
2145
/// <param name="jsonMessage">The json message to be passed to the handler.</param>
22-
/// <param name="cancellationToken">Cancellation token to cancel the async operation.</param>
2346
/// <returns>The json message returned by the handler.</returns>
47+
/// <remarks>Can be called concurrently with other message requests.</remarks>
2448
ValueTask<string> HandleExtensionWorkspaceMessageAsync(
2549
Solution solution,
2650
string messageName,
@@ -33,30 +57,11 @@ ValueTask<string> HandleExtensionWorkspaceMessageAsync(
3357
/// <param name="documentId">The document the message refers to.</param>
3458
/// <param name="messageName">The name of the handler to execute. This is generally the full name of the type implementing the handler.</param>
3559
/// <param name="jsonMessage">The json message to be passed to the handler.</param>
36-
/// <param name="cancellationToken">Cancellation token to cancel the async operation.</param>
3760
/// <returns>The json message returned by the handler.</returns>
61+
/// <remarks>Can be called concurrently with other message requests.</remarks>
3862
ValueTask<string> HandleExtensionDocumentMessageAsync(
3963
Document documentId,
4064
string messageName,
4165
string jsonMessage,
4266
CancellationToken cancellationToken);
43-
44-
/// <summary>
45-
/// Registers extension message handlers from the specified assembly.
46-
/// </summary>
47-
/// <param name="assemblyFilePath">The assembly to register and create message handlers from.</param>
48-
/// <returns>The names of the registered handlers.</returns>
49-
ValueTask<RegisterExtensionResponse> RegisterExtensionAsync(string assemblyFilePath, CancellationToken cancellationToken);
50-
51-
/// <summary>
52-
/// Unregisters extension message handlers previously registered from <paramref name="assemblyFilePath"/>.
53-
/// </summary>
54-
/// <param name="assemblyFilePath">The assembly for which handlers should be unregistered.</param>
55-
/// <returns>A task representing the async operation.</returns>
56-
ValueTask UnregisterExtensionAsync(string assemblyFilePath, CancellationToken cancellationToken);
57-
58-
/// <summary>
59-
/// Unregisters all extension message handlers.
60-
/// </summary>
61-
ValueTask ResetAsync(CancellationToken cancellationToken);
6267
}

src/Features/Core/Portable/Extensions/IExtensionMessageHandlerWrapper.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88

99
namespace Microsoft.CodeAnalysis.Extensions;
1010

11+
internal interface IExtensionMessageHandlerWrapper
12+
{
13+
}
14+
1115
/// <summary>
1216
/// Wrapper for an <c>IExtensionWorkspaceMessageHandler</c> or <c>IExtensionDocumentMessageHandler</c>
1317
/// as returned by <see cref="IExtensionMessageHandlerFactory"/>.
1418
/// </summary>
1519
/// <typeparam name="TArgument">The type of object received as parameter by the extension message
1620
/// handler.</typeparam>
17-
internal interface IExtensionMessageHandlerWrapper<TArgument>
21+
internal interface IExtensionMessageHandlerWrapper<TArgument> : IExtensionMessageHandlerWrapper
1822
{
1923
/// <summary>
2024
/// The type of object received as parameter by the extension message handler.

src/LanguageServer/Protocol/Handler/Extensions/ExtensionRegisterHandler.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ internal sealed class ExtensionRegisterHandler()
2020
{
2121
private const string MethodName = "roslyn/extensionRegister";
2222

23-
public bool MutatesSolutionState => false;
23+
/// <summary>
24+
/// Report that we mutate solution state so that we only attempt to register or unregister one extension at a time.
25+
/// This ensures we don't have to handle any threading concerns while this is happening. As this should be a rare
26+
/// operation, this simplifies things while ideally being low cost.
27+
/// </summary>
28+
public bool MutatesSolutionState => true;
2429

2530
public bool RequiresLSPSolution => true;
2631

src/LanguageServer/Protocol/Handler/Extensions/ExtensionUnregisterHandler.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ internal sealed class ExtensionUnregisterHandler()
2020
{
2121
private const string MethodName = "roslyn/extensionUnregister";
2222

23-
public bool MutatesSolutionState => false;
23+
/// <summary>
24+
/// Report that we mutate solution state so that we only attempt to register or unregister one extension at a time.
25+
/// This ensures we don't have to handle any threading concerns while this is happening. As this should be a rare
26+
/// operation, this simplifies things while ideally being low cost.
27+
/// </summary>
28+
public bool MutatesSolutionState => true;
2429

2530
public bool RequiresLSPSolution => true;
2631

src/LanguageServer/Protocol/Handler/ServerLifetime/LspServiceLifeCycleManager.cs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Threading.Tasks;
1010
using Microsoft.CodeAnalysis.Extensions;
1111
using Microsoft.CodeAnalysis.Host.Mef;
12-
using Microsoft.CodeAnalysis.Remote;
1312
using Microsoft.CommonLanguageServerProtocol.Framework;
1413
using Roslyn.LanguageServer.Protocol;
1514
using StreamJsonRpc;
@@ -41,21 +40,14 @@ private LspServiceLifeCycleManager(IClientLanguageServerManager clientLanguageSe
4140

4241
public async Task ShutdownAsync(string message = "Shutting down")
4342
{
43+
// Shutting down is not cancellable.
44+
var cancellationToken = CancellationToken.None;
45+
4446
var hostWorkspace = _lspWorkspaceRegistrationService.GetAllRegistrations().SingleOrDefault(w => w.Kind == WorkspaceKind.Host);
4547
if (hostWorkspace is not null)
4648
{
47-
var client = await RemoteHostClient.TryGetClientAsync(hostWorkspace, CancellationToken.None).ConfigureAwait(false);
48-
if (client is not null)
49-
{
50-
await client.TryInvokeAsync<IRemoteExtensionMessageHandlerService>(
51-
(service, cancellationToken) => service.ResetAsync(cancellationToken),
52-
CancellationToken.None).ConfigureAwait(false);
53-
}
54-
else
55-
{
56-
var service = hostWorkspace.Services.GetRequiredService<IExtensionMessageHandlerService>();
57-
service.Reset();
58-
}
49+
var service = hostWorkspace.Services.GetRequiredService<IExtensionMessageHandlerService>();
50+
await service.ResetAsync(cancellationToken).ConfigureAwait(false);
5951
}
6052

6153
try
@@ -65,7 +57,7 @@ await client.TryInvokeAsync<IRemoteExtensionMessageHandlerService>(
6557
MessageType = MessageType.Info,
6658
Message = message
6759
};
68-
await _clientLanguageServerManager.SendNotificationAsync("window/logMessage", messageParams, CancellationToken.None).ConfigureAwait(false);
60+
await _clientLanguageServerManager.SendNotificationAsync("window/logMessage", messageParams, cancellationToken).ConfigureAwait(false);
6961
}
7062
catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException)
7163
{

src/Tools/ExternalAccess/Extensions/Internal/ExtensionMessageHandlerFactory.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Immutable;
77
using System.Composition;
88
using System.Reflection;
9+
using System.Threading;
910
using Microsoft.CodeAnalysis.Host.Mef;
1011

1112
namespace Microsoft.CodeAnalysis.Extensions;
@@ -15,27 +16,34 @@ namespace Microsoft.CodeAnalysis.Extensions;
1516
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
1617
internal sealed class ExtensionMessageHandlerFactory() : IExtensionMessageHandlerFactory
1718
{
18-
public ImmutableArray<IExtensionMessageHandlerWrapper<Document>> CreateDocumentMessageHandlers(Assembly assembly, string extensionIdentifier)
19+
public ImmutableArray<IExtensionMessageHandlerWrapper<Document>> CreateDocumentMessageHandlers(
20+
Assembly assembly, string extensionIdentifier, CancellationToken cancellationToken)
1921
=> CreateWorkspaceHandlers(
2022
assembly,
2123
typeof(IExtensionDocumentMessageHandler<,>),
22-
(handler, handlerInterface) => new ExtensionDocumentMessageHandlerWrapper(handler, handlerInterface, extensionIdentifier));
24+
(handler, handlerInterface) => new ExtensionDocumentMessageHandlerWrapper(handler, handlerInterface, extensionIdentifier),
25+
cancellationToken);
2326

24-
public ImmutableArray<IExtensionMessageHandlerWrapper<Solution>> CreateWorkspaceMessageHandlers(Assembly assembly, string extensionIdentifier)
27+
public ImmutableArray<IExtensionMessageHandlerWrapper<Solution>> CreateWorkspaceMessageHandlers(
28+
Assembly assembly, string extensionIdentifier, CancellationToken cancellationToken)
2529
=> CreateWorkspaceHandlers(
2630
assembly,
2731
typeof(IExtensionWorkspaceMessageHandler<,>),
28-
(handler, handlerInterface) => new ExtensionWorkspaceMessageHandlerWrapper(handler, handlerInterface, extensionIdentifier));
32+
(handler, handlerInterface) => new ExtensionWorkspaceMessageHandlerWrapper(handler, handlerInterface, extensionIdentifier),
33+
cancellationToken);
2934

3035
private static ImmutableArray<IExtensionMessageHandlerWrapper<TArgument>> CreateWorkspaceHandlers<TArgument>(
3136
Assembly assembly,
3237
Type unboundInterfaceType,
33-
Func<object, Type, IExtensionMessageHandlerWrapper<TArgument>> wrapperCreator)
38+
Func<object, Type, IExtensionMessageHandlerWrapper<TArgument>> wrapperCreator,
39+
CancellationToken cancellationToken)
3440
{
3541
var resultBuilder = ImmutableArray.CreateBuilder<IExtensionMessageHandlerWrapper<TArgument>>();
3642

3743
foreach (var candidateType in assembly.GetTypes())
3844
{
45+
cancellationToken.ThrowIfCancellationRequested();
46+
3947
if (candidateType.IsAbstract || candidateType.IsGenericType)
4048
{
4149
continue;

src/Tools/ExternalAccess/Extensions/InternalAPI.Unshipped.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ Microsoft.CodeAnalysis.Extensions.ExtensionHandlerWrapper<TArgument>.Name.get ->
2727
Microsoft.CodeAnalysis.Extensions.ExtensionHandlerWrapper<TArgument>.ResponseType.get -> System.Type!
2828
Microsoft.CodeAnalysis.Extensions.ExtensionMessageContext.ExtensionMessageContext(Microsoft.CodeAnalysis.Solution! solution) -> void
2929
Microsoft.CodeAnalysis.Extensions.ExtensionMessageHandlerFactory
30-
Microsoft.CodeAnalysis.Extensions.ExtensionMessageHandlerFactory.CreateDocumentMessageHandlers(System.Reflection.Assembly! assembly, string! extensionIdentifier) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Extensions.IExtensionMessageHandlerWrapper<Microsoft.CodeAnalysis.Document!>!>
31-
Microsoft.CodeAnalysis.Extensions.ExtensionMessageHandlerFactory.CreateWorkspaceMessageHandlers(System.Reflection.Assembly! assembly, string! extensionIdentifier) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Extensions.IExtensionMessageHandlerWrapper<Microsoft.CodeAnalysis.Solution!>!>
30+
Microsoft.CodeAnalysis.Extensions.ExtensionMessageHandlerFactory.CreateDocumentMessageHandlers(System.Reflection.Assembly! assembly, string! extensionIdentifier, System.Threading.CancellationToken cancellationToken) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Extensions.IExtensionMessageHandlerWrapper<Microsoft.CodeAnalysis.Document!>!>
31+
Microsoft.CodeAnalysis.Extensions.ExtensionMessageHandlerFactory.CreateWorkspaceMessageHandlers(System.Reflection.Assembly! assembly, string! extensionIdentifier, System.Threading.CancellationToken cancellationToken) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Extensions.IExtensionMessageHandlerWrapper<Microsoft.CodeAnalysis.Solution!>!>
3232
Microsoft.CodeAnalysis.Extensions.ExtensionMessageHandlerFactory.ExtensionMessageHandlerFactory() -> void
3333
Microsoft.CodeAnalysis.Extensions.ExtensionWorkspaceMessageHandlerWrapper
3434
Microsoft.CodeAnalysis.Extensions.ExtensionWorkspaceMessageHandlerWrapper.ExtensionWorkspaceMessageHandlerWrapper(object! handler, System.Type! customMessageHandlerInterface, string! extensionIdentifier) -> void

0 commit comments

Comments
 (0)