diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs
index 2f3390cc0cd62..328e663df7978 100644
--- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs
+++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs
@@ -11,6 +11,18 @@
namespace Microsoft.CodeAnalysis.ErrorReporting
{
+ ///
+ /// Thrown when async code must cancel the current execution but does not have access to the of the passed to the code.
+ /// Should be used in very rare cases where the is out of our control (e.g. owned but not exposed by JSON RPC in certain call-back scenarios).
+ ///
+ internal sealed class OperationCanceledIgnoringCallerTokenException : OperationCanceledException
+ {
+ public OperationCanceledIgnoringCallerTokenException(Exception innerException)
+ : base(innerException.Message, innerException)
+ {
+ }
+ }
+
internal static class FatalError
{
public delegate void ErrorReporterHandler(Exception exception, ErrorSeverity severity, bool forceDump);
@@ -124,7 +136,7 @@ public static bool ReportAndPropagateUnlessCanceled(Exception exception, ErrorSe
[DebuggerHidden]
public static bool ReportAndPropagateUnlessCanceled(Exception exception, CancellationToken contextCancellationToken, ErrorSeverity severity = ErrorSeverity.Uncategorized)
{
- if (ExceptionUtilities.IsCurrentOperationBeingCancelled(exception, contextCancellationToken))
+ if (ExceptionUtilities.IsCurrentOperationBeingCancelled(exception, contextCancellationToken) || exception is OperationCanceledIgnoringCallerTokenException)
{
return false;
}
@@ -200,7 +212,7 @@ public static bool ReportAndCatchUnlessCanceled(Exception exception, ErrorSeveri
[DebuggerHidden]
public static bool ReportAndCatchUnlessCanceled(Exception exception, CancellationToken contextCancellationToken, ErrorSeverity severity = ErrorSeverity.Uncategorized)
{
- if (ExceptionUtilities.IsCurrentOperationBeingCancelled(exception, contextCancellationToken))
+ if (ExceptionUtilities.IsCurrentOperationBeingCancelled(exception, contextCancellationToken) || exception is OperationCanceledIgnoringCallerTokenException)
{
return false;
}
diff --git a/src/Workspaces/Remote/Core/RemoteCallback.cs b/src/Workspaces/Remote/Core/RemoteCallback.cs
index 5444ee902333c..5c9cdadd97a99 100644
--- a/src/Workspaces/Remote/Core/RemoteCallback.cs
+++ b/src/Workspaces/Remote/Core/RemoteCallback.cs
@@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
+using Microsoft.ServiceHub.Framework;
using Roslyn.Utilities;
using StreamJsonRpc;
@@ -17,8 +18,6 @@ namespace Microsoft.CodeAnalysis.Remote
/// Wraps calls from a remote brokered service back to the client or to an in-proc brokered service.
/// The purpose of this type is to handle exceptions thrown by the underlying remoting infrastructure
/// in manner that's compatible with our exception handling policies.
- ///
- /// TODO: This wrapper might not be needed once https://github.com/microsoft/vs-streamjsonrpc/issues/246 is fixed.
///
internal readonly struct RemoteCallback
where T : class
@@ -30,6 +29,36 @@ public RemoteCallback(T callback)
_callback = callback;
}
+ ///
+ /// Use to perform a callback from ServiceHub process to an arbitrary brokered service hosted in the original process (usually devenv).
+ ///
+ public static async ValueTask InvokeServiceAsync(
+ ServiceBrokerClient client,
+ ServiceRpcDescriptor serviceDescriptor,
+ Func, CancellationToken, ValueTask> invocation,
+ CancellationToken cancellationToken)
+ {
+ ServiceBrokerClient.Rental rental;
+ try
+ {
+ rental = await client.GetProxyAsync(serviceDescriptor, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ObjectDisposedException e)
+ {
+ // When a connection is dropped ServiceHub's ServiceManager disposes the brokered service, which in turn disposes the ServiceBrokerClient.
+ cancellationToken.ThrowIfCancellationRequested();
+ throw new OperationCanceledIgnoringCallerTokenException(e);
+ }
+
+ Contract.ThrowIfNull(rental.Proxy);
+ var callback = new RemoteCallback(rental.Proxy);
+
+ return await invocation(callback, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Invokes API on the callback object hosted in the original process (usually devenv) associated with the currently executing brokered service hosted in ServiceHub process.
+ ///
public async ValueTask InvokeAsync(Func invocation, CancellationToken cancellationToken)
{
try
@@ -42,6 +71,9 @@ public async ValueTask InvokeAsync(Func invocat
}
}
+ ///
+ /// Invokes API on the callback object hosted in the original process (usually devenv) associated with the currently executing brokered service hosted in ServiceHub process.
+ ///
public async ValueTask InvokeAsync(Func> invocation, CancellationToken cancellationToken)
{
try
@@ -55,7 +87,8 @@ public async ValueTask InvokeAsync(Func
- /// Invokes a remote API that streams results back to the caller.
+ /// Invokes API on the callback object hosted in the original process (usually devenv) associated with the currently executing brokered service hosted in ServiceHub process.
+ /// The API streams results back to the caller.
///
///
public async ValueTask InvokeAsync(
@@ -100,12 +133,9 @@ private static bool ReportUnexpectedException(Exception exception, CancellationT
return FatalError.ReportAndCatch(exception);
}
- // When a connection is dropped and CancelLocallyInvokedMethodsWhenConnectionIsClosed is
- // set ConnectionLostException should not be thrown. Instead the cancellation token should be
- // signaled and OperationCancelledException should be thrown.
- // Seems to not work in all cases currently, so we need to cancel ourselves (bug https://github.com/microsoft/vs-streamjsonrpc/issues/551).
- // Once this issue is fixed we can remov this if statement and fall back to reporting NFW
- // as any observation of ConnectionLostException indicates a bug (e.g. https://github.com/microsoft/vs-streamjsonrpc/issues/549).
+ // When a connection is dropped we can see ConnectionLostException even though CancelLocallyInvokedMethodsWhenConnectionIsClosed is set.
+ // That's because there might be a delay between the JsonRpc detecting the disconnect and the call attempting to send a message.
+ // Catch the ConnectionLostException exception here and convert it to OperationCanceledException.
if (exception is ConnectionLostException)
{
return true;
@@ -121,7 +151,7 @@ private static Exception OnUnexpectedException(Exception exception, Cancellation
if (exception is ConnectionLostException)
{
- throw new OperationCanceledException(exception.Message, exception);
+ throw new OperationCanceledIgnoringCallerTokenException(exception);
}
// If this is hit the cancellation token passed to the service implementation did not use the correct token,
diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs
index 5a57598030c04..5485e3977233d 100644
--- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs
+++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs
@@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.Remote
{
@@ -32,26 +33,24 @@ private readonly struct SolutionCreator
private readonly AssetProvider _assetProvider;
private readonly Solution _baseSolution;
- private readonly CancellationToken _cancellationToken;
- public SolutionCreator(HostServices hostServices, AssetProvider assetService, Solution baseSolution, CancellationToken cancellationToken)
+ public SolutionCreator(HostServices hostServices, AssetProvider assetService, Solution baseSolution)
{
_hostServices = hostServices;
_assetProvider = assetService;
_baseSolution = baseSolution;
- _cancellationToken = cancellationToken;
}
- public async Task IsIncrementalUpdateAsync(Checksum newSolutionChecksum)
+ public async Task IsIncrementalUpdateAsync(Checksum newSolutionChecksum, CancellationToken cancellationToken)
{
- var newSolutionChecksums = await _assetProvider.GetAssetAsync(newSolutionChecksum, _cancellationToken).ConfigureAwait(false);
- var newSolutionInfo = await _assetProvider.GetAssetAsync(newSolutionChecksums.Attributes, _cancellationToken).ConfigureAwait(false);
+ var newSolutionChecksums = await _assetProvider.GetAssetAsync(newSolutionChecksum, cancellationToken).ConfigureAwait(false);
+ var newSolutionInfo = await _assetProvider.GetAssetAsync(newSolutionChecksums.Attributes, cancellationToken).ConfigureAwait(false);
// if either solution id or file path changed, then we consider it as new solution
return _baseSolution.Id == newSolutionInfo.Id && _baseSolution.FilePath == newSolutionInfo.FilePath;
}
- public async Task CreateSolutionAsync(Checksum newSolutionChecksum)
+ public async Task CreateSolutionAsync(Checksum newSolutionChecksum, CancellationToken cancellationToken)
{
try
{
@@ -61,12 +60,12 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum)
// if needed again later.
solution = solution.WithoutFrozenSourceGeneratedDocuments();
- var oldSolutionChecksums = await solution.State.GetStateChecksumsAsync(_cancellationToken).ConfigureAwait(false);
- var newSolutionChecksums = await _assetProvider.GetAssetAsync(newSolutionChecksum, _cancellationToken).ConfigureAwait(false);
+ var oldSolutionChecksums = await solution.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);
+ var newSolutionChecksums = await _assetProvider.GetAssetAsync(newSolutionChecksum, cancellationToken).ConfigureAwait(false);
if (oldSolutionChecksums.Attributes != newSolutionChecksums.Attributes)
{
- var newSolutionInfo = await _assetProvider.GetAssetAsync(newSolutionChecksums.Attributes, _cancellationToken).ConfigureAwait(false);
+ var newSolutionInfo = await _assetProvider.GetAssetAsync(newSolutionChecksums.Attributes, cancellationToken).ConfigureAwait(false);
// if either id or file path has changed, then this is not update
Contract.ThrowIfFalse(solution.Id == newSolutionInfo.Id && solution.FilePath == newSolutionInfo.FilePath);
@@ -74,26 +73,26 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum)
if (oldSolutionChecksums.Projects.Checksum != newSolutionChecksums.Projects.Checksum)
{
- solution = await UpdateProjectsAsync(solution, oldSolutionChecksums.Projects, newSolutionChecksums.Projects).ConfigureAwait(false);
+ solution = await UpdateProjectsAsync(solution, oldSolutionChecksums.Projects, newSolutionChecksums.Projects, cancellationToken).ConfigureAwait(false);
}
if (oldSolutionChecksums.AnalyzerReferences.Checksum != newSolutionChecksums.AnalyzerReferences.Checksum)
{
solution = solution.WithAnalyzerReferences(await _assetProvider.CreateCollectionAsync(
- newSolutionChecksums.AnalyzerReferences, _cancellationToken).ConfigureAwait(false));
+ newSolutionChecksums.AnalyzerReferences, cancellationToken).ConfigureAwait(false));
}
if (newSolutionChecksums.FrozenSourceGeneratedDocumentIdentity != Checksum.Null && newSolutionChecksums.FrozenSourceGeneratedDocumentText != Checksum.Null)
{
- var identity = await _assetProvider.GetAssetAsync(newSolutionChecksums.FrozenSourceGeneratedDocumentIdentity, _cancellationToken).ConfigureAwait(false);
- var serializableSourceText = await _assetProvider.GetAssetAsync(newSolutionChecksums.FrozenSourceGeneratedDocumentText, _cancellationToken).ConfigureAwait(false);
- var sourceText = await serializableSourceText.GetTextAsync(_cancellationToken).ConfigureAwait(false);
+ var identity = await _assetProvider.GetAssetAsync(newSolutionChecksums.FrozenSourceGeneratedDocumentIdentity, cancellationToken).ConfigureAwait(false);
+ var serializableSourceText = await _assetProvider.GetAssetAsync(newSolutionChecksums.FrozenSourceGeneratedDocumentText, cancellationToken).ConfigureAwait(false);
+ var sourceText = await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false);
solution = solution.WithFrozenSourceGeneratedDocument(identity, sourceText).Project.Solution;
}
#if DEBUG
// make sure created solution has same checksum as given one
- await ValidateChecksumAsync(newSolutionChecksum, solution).ConfigureAwait(false);
+ await ValidateChecksumAsync(newSolutionChecksum, solution, cancellationToken).ConfigureAwait(false);
#endif
return solution;
@@ -104,7 +103,7 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum)
}
}
- private async Task UpdateProjectsAsync(Solution solution, ChecksumCollection oldChecksums, ChecksumCollection newChecksums)
+ private async Task UpdateProjectsAsync(Solution solution, ChecksumCollection oldChecksums, ChecksumCollection newChecksums, CancellationToken cancellationToken)
{
using var olds = SharedPools.Default>().GetPooledObject();
using var news = SharedPools.Default>().GetPooledObject();
@@ -116,23 +115,23 @@ private async Task UpdateProjectsAsync(Solution solution, ChecksumColl
olds.Object.ExceptWith(newChecksums);
news.Object.ExceptWith(oldChecksums);
- return await UpdateProjectsAsync(solution, olds.Object, news.Object).ConfigureAwait(false);
+ return await UpdateProjectsAsync(solution, olds.Object, news.Object, cancellationToken).ConfigureAwait(false);
}
- private async Task UpdateProjectsAsync(Solution solution, HashSet oldChecksums, HashSet newChecksums)
+ private async Task UpdateProjectsAsync(Solution solution, HashSet oldChecksums, HashSet newChecksums, CancellationToken cancellationToken)
{
- var oldMap = await GetProjectMapAsync(solution, oldChecksums).ConfigureAwait(false);
- var newMap = await GetProjectMapAsync(_assetProvider, newChecksums).ConfigureAwait(false);
+ var oldMap = await GetProjectMapAsync(solution, oldChecksums, cancellationToken).ConfigureAwait(false);
+ var newMap = await GetProjectMapAsync(_assetProvider, newChecksums, cancellationToken).ConfigureAwait(false);
// bulk sync assets
- await SynchronizeAssetsAsync(oldMap, newMap).ConfigureAwait(false);
+ await SynchronizeAssetsAsync(oldMap, newMap, cancellationToken).ConfigureAwait(false);
// added project
foreach (var (projectId, newProjectChecksums) in newMap)
{
if (!oldMap.ContainsKey(projectId))
{
- var projectInfo = await _assetProvider.CreateProjectInfoAsync(newProjectChecksums.Checksum, _cancellationToken).ConfigureAwait(false);
+ var projectInfo = await _assetProvider.CreateProjectInfoAsync(newProjectChecksums.Checksum, cancellationToken).ConfigureAwait(false);
if (projectInfo == null)
{
// this project is not supported in OOP
@@ -179,13 +178,13 @@ private async Task UpdateProjectsAsync(Solution solution, HashSet oldMap, Dictionary newMap)
+ private async Task SynchronizeAssetsAsync(Dictionary oldMap, Dictionary newMap, CancellationToken cancellationToken)
{
using var pooledObject = SharedPools.Default>().GetPooledObject();
@@ -200,15 +199,15 @@ private async Task SynchronizeAssetsAsync(Dictionary UpdateProjectAsync(Project project, ProjectStateChecksums oldProjectChecksums, ProjectStateChecksums newProjectChecksums)
+ private async Task UpdateProjectAsync(Project project, ProjectStateChecksums oldProjectChecksums, ProjectStateChecksums newProjectChecksums, CancellationToken cancellationToken)
{
// changed info
if (oldProjectChecksums.Info != newProjectChecksums.Info)
{
- project = await UpdateProjectInfoAsync(project, newProjectChecksums.Info).ConfigureAwait(false);
+ project = await UpdateProjectInfoAsync(project, newProjectChecksums.Info, cancellationToken).ConfigureAwait(false);
}
// changed compilation options
@@ -217,34 +216,34 @@ private async Task UpdateProjectAsync(Project project, ProjectStateChe
project = project.WithCompilationOptions(
project.State.ProjectInfo.Attributes.FixUpCompilationOptions(
await _assetProvider.GetAssetAsync(
- newProjectChecksums.CompilationOptions, _cancellationToken).ConfigureAwait(false)));
+ newProjectChecksums.CompilationOptions, cancellationToken).ConfigureAwait(false)));
}
// changed parse options
if (oldProjectChecksums.ParseOptions != newProjectChecksums.ParseOptions)
{
- project = project.WithParseOptions(await _assetProvider.GetAssetAsync(newProjectChecksums.ParseOptions, _cancellationToken).ConfigureAwait(false));
+ project = project.WithParseOptions(await _assetProvider.GetAssetAsync(newProjectChecksums.ParseOptions, cancellationToken).ConfigureAwait(false));
}
// changed project references
if (oldProjectChecksums.ProjectReferences.Checksum != newProjectChecksums.ProjectReferences.Checksum)
{
project = project.WithProjectReferences(await _assetProvider.CreateCollectionAsync(
- newProjectChecksums.ProjectReferences, _cancellationToken).ConfigureAwait(false));
+ newProjectChecksums.ProjectReferences, cancellationToken).ConfigureAwait(false));
}
// changed metadata references
if (oldProjectChecksums.MetadataReferences.Checksum != newProjectChecksums.MetadataReferences.Checksum)
{
project = project.WithMetadataReferences(await _assetProvider.CreateCollectionAsync(
- newProjectChecksums.MetadataReferences, _cancellationToken).ConfigureAwait(false));
+ newProjectChecksums.MetadataReferences, cancellationToken).ConfigureAwait(false));
}
// changed analyzer references
if (oldProjectChecksums.AnalyzerReferences.Checksum != newProjectChecksums.AnalyzerReferences.Checksum)
{
project = project.WithAnalyzerReferences(await _assetProvider.CreateCollectionAsync(
- newProjectChecksums.AnalyzerReferences, _cancellationToken).ConfigureAwait(false));
+ newProjectChecksums.AnalyzerReferences, cancellationToken).ConfigureAwait(false));
}
// changed analyzer references
@@ -257,7 +256,8 @@ await _assetProvider.GetAssetAsync(
oldProjectChecksums.Documents,
newProjectChecksums.Documents,
(solution, documents) => solution.AddDocuments(documents),
- (solution, documentIds) => solution.RemoveDocuments(documentIds)).ConfigureAwait(false);
+ (solution, documentIds) => solution.RemoveDocuments(documentIds),
+ cancellationToken).ConfigureAwait(false);
}
// changed additional documents
@@ -270,7 +270,8 @@ await _assetProvider.GetAssetAsync(
oldProjectChecksums.AdditionalDocuments,
newProjectChecksums.AdditionalDocuments,
(solution, documents) => solution.AddAdditionalDocuments(documents),
- (solution, documentIds) => solution.RemoveAdditionalDocuments(documentIds)).ConfigureAwait(false);
+ (solution, documentIds) => solution.RemoveAdditionalDocuments(documentIds),
+ cancellationToken).ConfigureAwait(false);
}
// changed analyzer config documents
@@ -283,15 +284,16 @@ await _assetProvider.GetAssetAsync(
oldProjectChecksums.AnalyzerConfigDocuments,
newProjectChecksums.AnalyzerConfigDocuments,
(solution, documents) => solution.AddAnalyzerConfigDocuments(documents),
- (solution, documentIds) => solution.RemoveAnalyzerConfigDocuments(documentIds)).ConfigureAwait(false);
+ (solution, documentIds) => solution.RemoveAnalyzerConfigDocuments(documentIds),
+ cancellationToken).ConfigureAwait(false);
}
return project.Solution;
}
- private async Task UpdateProjectInfoAsync(Project project, Checksum infoChecksum)
+ private async Task UpdateProjectInfoAsync(Project project, Checksum infoChecksum, CancellationToken cancellationToken)
{
- var newProjectAttributes = await _assetProvider.GetAssetAsync(infoChecksum, _cancellationToken).ConfigureAwait(false);
+ var newProjectAttributes = await _assetProvider.GetAssetAsync(infoChecksum, cancellationToken).ConfigureAwait(false);
// there is no API to change these once project is created
Contract.ThrowIfFalse(project.State.ProjectInfo.Attributes.Id == newProjectAttributes.Id);
@@ -355,7 +357,8 @@ private async Task UpdateDocumentsAsync(
ChecksumCollection oldChecksums,
ChecksumCollection newChecksums,
Func, Solution> addDocuments,
- Func, Solution> removeDocuments)
+ Func, Solution> removeDocuments,
+ CancellationToken cancellationToken)
{
using var olds = SharedPools.Default>().GetPooledObject();
using var news = SharedPools.Default>().GetPooledObject();
@@ -367,15 +370,15 @@ private async Task UpdateDocumentsAsync(
olds.Object.ExceptWith(newChecksums);
news.Object.ExceptWith(oldChecksums);
- var oldMap = await GetDocumentMapAsync(existingTextDocumentStates, olds.Object).ConfigureAwait(false);
- var newMap = await GetDocumentMapAsync(_assetProvider, news.Object).ConfigureAwait(false);
+ var oldMap = await GetDocumentMapAsync(existingTextDocumentStates, olds.Object, cancellationToken).ConfigureAwait(false);
+ var newMap = await GetDocumentMapAsync(_assetProvider, news.Object, cancellationToken).ConfigureAwait(false);
// If more than two documents changed during a single update, perform a bulk synchronization on the
// project to avoid large numbers of small synchronization calls during document updates.
// 🔗 https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1365014
if (newMap.Count > 2)
{
- await _assetProvider.SynchronizeProjectAssetsAsync(new[] { projectChecksums.Checksum }, _cancellationToken).ConfigureAwait(false);
+ await _assetProvider.SynchronizeProjectAssetsAsync(new[] { projectChecksums.Checksum }, cancellationToken).ConfigureAwait(false);
}
// added document
@@ -387,7 +390,7 @@ private async Task UpdateDocumentsAsync(
lazyDocumentsToAdd ??= ImmutableArray.CreateBuilder();
// we have new document added
- var documentInfo = await _assetProvider.CreateDocumentInfoAsync(newDocumentChecksums.Checksum, _cancellationToken).ConfigureAwait(false);
+ var documentInfo = await _assetProvider.CreateDocumentInfoAsync(newDocumentChecksums.Checksum, cancellationToken).ConfigureAwait(false);
lazyDocumentsToAdd.Add(documentInfo);
}
}
@@ -410,7 +413,7 @@ private async Task UpdateDocumentsAsync(
var document = project.GetDocument(documentId) ?? project.GetAdditionalDocument(documentId) ?? project.GetAnalyzerConfigDocument(documentId);
Contract.ThrowIfNull(document);
- project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums).ConfigureAwait(false);
+ project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, cancellationToken).ConfigureAwait(false);
}
// removed document
@@ -433,19 +436,19 @@ private async Task UpdateDocumentsAsync(
return project;
}
- private async Task UpdateDocumentAsync(TextDocument document, DocumentStateChecksums oldDocumentChecksums, DocumentStateChecksums newDocumentChecksums)
+ private async Task UpdateDocumentAsync(TextDocument document, DocumentStateChecksums oldDocumentChecksums, DocumentStateChecksums newDocumentChecksums, CancellationToken cancellationToken)
{
// changed info
if (oldDocumentChecksums.Info != newDocumentChecksums.Info)
{
- document = await UpdateDocumentInfoAsync(document, newDocumentChecksums.Info).ConfigureAwait(false);
+ document = await UpdateDocumentInfoAsync(document, newDocumentChecksums.Info, cancellationToken).ConfigureAwait(false);
}
// changed text
if (oldDocumentChecksums.Text != newDocumentChecksums.Text)
{
- var serializableSourceText = await _assetProvider.GetAssetAsync(newDocumentChecksums.Text, _cancellationToken).ConfigureAwait(false);
- var sourceText = await serializableSourceText.GetTextAsync(_cancellationToken).ConfigureAwait(false);
+ var serializableSourceText = await _assetProvider.GetAssetAsync(newDocumentChecksums.Text, cancellationToken).ConfigureAwait(false);
+ var sourceText = await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false);
document = document.Kind switch
{
@@ -459,9 +462,9 @@ private async Task UpdateDocumentAsync(TextDocument document, DocumentS
return document.Project;
}
- private async Task UpdateDocumentInfoAsync(TextDocument document, Checksum infoChecksum)
+ private async Task UpdateDocumentInfoAsync(TextDocument document, Checksum infoChecksum, CancellationToken cancellationToken)
{
- var newDocumentInfo = await _assetProvider.GetAssetAsync(infoChecksum, _cancellationToken).ConfigureAwait(false);
+ var newDocumentInfo = await _assetProvider.GetAssetAsync(infoChecksum, cancellationToken).ConfigureAwait(false);
// there is no api to change these once document is created
Contract.ThrowIfFalse(document.State.Attributes.Id == newDocumentInfo.Id);
@@ -487,31 +490,31 @@ private async Task UpdateDocumentInfoAsync(TextDocument document,
return document;
}
- private async Task> GetDocumentMapAsync(AssetProvider assetProvider, HashSet documents)
+ private static async Task> GetDocumentMapAsync(AssetProvider assetProvider, HashSet documents, CancellationToken cancellationToken)
{
var map = new Dictionary();
- var documentChecksums = await assetProvider.GetAssetsAsync(documents, _cancellationToken).ConfigureAwait(false);
- var infos = await assetProvider.GetAssetsAsync(documentChecksums.Select(p => p.Item2.Info), _cancellationToken).ConfigureAwait(false);
+ var documentChecksums = await assetProvider.GetAssetsAsync(documents, cancellationToken).ConfigureAwait(false);
+ var infos = await assetProvider.GetAssetsAsync(documentChecksums.Select(p => p.Item2.Info), cancellationToken).ConfigureAwait(false);
foreach (var kv in documentChecksums)
{
Debug.Assert(assetProvider.EnsureCacheEntryIfExists(kv.Item2.Info), "Expected the prior call to GetAssetsAsync to obtain all items for this loop.");
- var info = await assetProvider.GetAssetAsync(kv.Item2.Info, _cancellationToken).ConfigureAwait(false);
+ var info = await assetProvider.GetAssetAsync(kv.Item2.Info, cancellationToken).ConfigureAwait(false);
map.Add(info.Id, kv.Item2);
}
return map;
}
- private async Task> GetDocumentMapAsync(IEnumerable states, HashSet documents)
+ private static async Task> GetDocumentMapAsync(IEnumerable states, HashSet documents, CancellationToken cancellationToken)
{
var map = new Dictionary();
foreach (var state in states)
{
- var documentChecksums = await state.GetStateChecksumsAsync(_cancellationToken).ConfigureAwait(false);
+ var documentChecksums = await state.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);
if (documents.Contains(documentChecksums.Checksum))
{
map.Add(state.Id, documentChecksums);
@@ -521,29 +524,29 @@ private async Task> GetDocumentMa
return map;
}
- private async Task> GetProjectMapAsync(AssetProvider assetProvider, HashSet projects)
+ private static async Task> GetProjectMapAsync(AssetProvider assetProvider, HashSet projects, CancellationToken cancellationToken)
{
var map = new Dictionary();
- var projectChecksums = await assetProvider.GetAssetsAsync(projects, _cancellationToken).ConfigureAwait(false);
- var infos = await assetProvider.GetAssetsAsync(projectChecksums.Select(p => p.Item2.Info), _cancellationToken).ConfigureAwait(false);
+ var projectChecksums = await assetProvider.GetAssetsAsync(projects, cancellationToken).ConfigureAwait(false);
+ var infos = await assetProvider.GetAssetsAsync(projectChecksums.Select(p => p.Item2.Info), cancellationToken).ConfigureAwait(false);
foreach (var kv in projectChecksums)
{
- var info = await assetProvider.GetAssetAsync(kv.Item2.Info, _cancellationToken).ConfigureAwait(false);
+ var info = await assetProvider.GetAssetAsync(kv.Item2.Info, cancellationToken).ConfigureAwait(false);
map.Add(info.Id, kv.Item2);
}
return map;
}
- private async Task> GetProjectMapAsync(Solution solution, HashSet projects)
+ private static async Task> GetProjectMapAsync(Solution solution, HashSet projects, CancellationToken cancellationToken)
{
var map = new Dictionary();
foreach (var (projectId, projectState) in solution.State.ProjectStates)
{
- var projectChecksums = await projectState.GetStateChecksumsAsync(_cancellationToken).ConfigureAwait(false);
+ var projectChecksums = await projectState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);
if (projects.Contains(projectChecksums.Checksum))
{
map.Add(projectId, projectChecksums);
@@ -554,7 +557,7 @@ private async Task> GetProjectMapAs
}
#if DEBUG
- private async Task ValidateChecksumAsync(Checksum checksumFromRequest, Solution incrementalSolutionBuilt)
+ private async Task ValidateChecksumAsync(Checksum checksumFromRequest, Solution incrementalSolutionBuilt, CancellationToken cancellationToken)
{
var currentSolutionChecksum = await incrementalSolutionBuilt.State.GetChecksumAsync(CancellationToken.None).ConfigureAwait(false);
if (checksumFromRequest == currentSolutionChecksum)
@@ -562,7 +565,7 @@ private async Task ValidateChecksumAsync(Checksum checksumFromRequest, Solution
return;
}
- var solutionInfo = await _assetProvider.CreateSolutionInfoAsync(checksumFromRequest, _cancellationToken).ConfigureAwait(false);
+ var solutionInfo = await _assetProvider.CreateSolutionInfoAsync(checksumFromRequest, cancellationToken).ConfigureAwait(false);
var workspace = new AdhocWorkspace(_hostServices);
workspace.AddSolution(solutionInfo);
diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs
index 6eb53d3a34bcd..52cc5f7e09f9d 100644
--- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs
+++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs
@@ -110,7 +110,6 @@ await SlowGetSolutionAndRunAsync(
/// the same .
///
///
-
public ValueTask<(Solution solution, T result)> RunWithSolutionAsync(
AssetProvider assetProvider,
Checksum solutionChecksum,
@@ -285,13 +284,13 @@ private async Task ComputeSolutionAsync(
{
try
{
- var updater = new SolutionCreator(Services.HostServices, assetProvider, currentSolution, cancellationToken);
+ var updater = new SolutionCreator(Services.HostServices, assetProvider, currentSolution);
// check whether solution is update to the given base solution
- if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
+ if (await updater.IsIncrementalUpdateAsync(solutionChecksum, cancellationToken).ConfigureAwait(false))
{
// create updated solution off the baseSolution
- return await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false);
+ return await updater.CreateSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
}
// we need new solution. bulk sync all asset for the solution first.
diff --git a/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs
index 37cfcc6e7940d..395edd26cdf44 100644
--- a/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs
+++ b/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs
@@ -2,15 +2,19 @@
// 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.Collections.Generic;
using System.Collections.Immutable;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Serialization;
using Microsoft.ServiceHub.Framework;
using Microsoft.VisualStudio.Threading;
using Roslyn.Utilities;
+using StreamJsonRpc;
namespace Microsoft.CodeAnalysis.Remote
{
@@ -28,12 +32,13 @@ public SolutionAssetSource(ServiceBrokerClient client)
// Make sure we are on the thread pool to avoid UI thread dependencies if external code uses ConfigureAwait(true)
await TaskScheduler.Default;
- using var provider = await _client.GetProxyAsync(SolutionAssetProvider.ServiceDescriptor, cancellationToken).ConfigureAwait(false);
- Contract.ThrowIfNull(provider.Proxy);
-
- return await new RemoteCallback(provider.Proxy).InvokeAsync(
- (proxy, pipeWriter, cancellationToken) => proxy.GetAssetsAsync(pipeWriter, solutionChecksum, checksums.ToArray(), cancellationToken),
- (pipeReader, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(pipeReader, solutionChecksum, checksums, serializerService, cancellationToken),
+ return await RemoteCallback.InvokeServiceAsync(
+ _client,
+ SolutionAssetProvider.ServiceDescriptor,
+ (callback, cancellationToken) => callback.InvokeAsync(
+ (proxy, pipeWriter, cancellationToken) => proxy.GetAssetsAsync(pipeWriter, solutionChecksum, checksums.ToArray(), cancellationToken),
+ (pipeReader, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(pipeReader, solutionChecksum, checksums, serializerService, cancellationToken),
+ cancellationToken),
cancellationToken).ConfigureAwait(false);
}
}