Skip to content

Commit 57759e3

Browse files
committed
Do not cancel operations after point of no return
1 parent 24bcf13 commit 57759e3

File tree

3 files changed

+19
-4
lines changed

3 files changed

+19
-4
lines changed

src/Workspaces/Remote/Core/BrokeredServiceConnection.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ internal static async ValueTask<TResult> InvokeStreamingServiceAsync<TResult>(
150150
Func<PipeReader, CancellationToken, ValueTask<TResult>> reader,
151151
CancellationToken cancellationToken)
152152
{
153+
// We can cancel at entry, but once the pipe operations are scheduled we rely on both operations running to
154+
// avoid deadlocks (the exception handler in 'writerTask' ensures progress is made in 'readerTask').
155+
cancellationToken.ThrowIfCancellationRequested();
156+
var mustNotCancelToken = CancellationToken.None;
157+
153158
var pipe = new Pipe();
154159

155160
// Create new tasks that both start executing, rather than invoking the delegates directly
@@ -170,7 +175,7 @@ internal static async ValueTask<TResult> InvokeStreamingServiceAsync<TResult>(
170175

171176
throw;
172177
}
173-
}, cancellationToken);
178+
}, mustNotCancelToken);
174179

175180
var readerTask = Task.Run(
176181
async () =>
@@ -189,7 +194,7 @@ internal static async ValueTask<TResult> InvokeStreamingServiceAsync<TResult>(
189194
{
190195
await pipe.Reader.CompleteAsync(exception).ConfigureAwait(false);
191196
}
192-
}, cancellationToken);
197+
}, mustNotCancelToken);
193198

194199
await Task.WhenAll(writerTask, readerTask).ConfigureAwait(false);
195200

src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ static void WriteAsset(ObjectWriter writer, ISerializerService serializer, Check
8888

8989
public static async ValueTask<ImmutableArray<(Checksum, object)>> ReadDataAsync(PipeReader pipeReader, int scopeId, ISet<Checksum> checksums, ISerializerService serializerService, CancellationToken cancellationToken)
9090
{
91+
// We can cancel at entry, but once the pipe operations are scheduled we rely on both operations running to
92+
// avoid deadlocks (the exception handler in 'copyTask' ensures progress is made in the blocking read).
93+
cancellationToken.ThrowIfCancellationRequested();
94+
var mustNotCancelToken = CancellationToken.None;
95+
9196
// Workaround for ObjectReader not supporting async reading.
9297
// Unless we read from the RPC stream asynchronously and with cancallation support we might hang when the server cancels.
9398
// https://github.com/dotnet/roslyn/issues/47861
@@ -113,7 +118,7 @@ static void WriteAsset(ObjectWriter writer, ISerializerService serializer, Check
113118
await localPipe.Writer.CompleteAsync(exception).ConfigureAwait(false);
114119
await pipeReader.CompleteAsync(exception).ConfigureAwait(false);
115120
}
116-
}, cancellationToken);
121+
}, mustNotCancelToken);
117122

118123
// blocking read from the local pipe on the current thread:
119124
try

src/Workspaces/Remote/Core/SolutionAssetProvider.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ public async ValueTask GetAssetsAsync(PipeWriter pipeWriter, int scopeId, Checks
5252
assetMap = await assetStorage.GetAssetsAsync(scopeId, checksums, cancellationToken).ConfigureAwait(false);
5353
}
5454

55+
// We can cancel early, but once the pipe operations are scheduled we rely on both operations running to
56+
// avoid deadlocks (the exception handler in 'task1' ensures progress is made in 'task2').
57+
cancellationToken.ThrowIfCancellationRequested();
58+
var mustNotCancelToken = CancellationToken.None;
59+
5560
// Work around the lack of async stream writing in ObjectWriter, which is required when writing to the RPC pipe.
5661
// Run two tasks - the first synchronously writes to a local pipe and the second asynchronosly transfers the data to the RPC pipe.
5762
//
@@ -71,7 +76,7 @@ public async ValueTask GetAssetsAsync(PipeWriter pipeWriter, int scopeId, Checks
7176
{
7277
// no-op
7378
}
74-
}, cancellationToken);
79+
}, mustNotCancelToken);
7580

7681
// Complete RPC once we send the initial piece of data and start waiting for the writer to send more,
7782
// so the client can start reading from the stream. Once CopyPipeDataAsync completes the pipeWriter

0 commit comments

Comments
 (0)