Skip to content

Commit d86be87

Browse files
authored
Enable CA2012 (Use ValueTask Correctly) (#31221)
1 parent f5feab7 commit d86be87

File tree

17 files changed

+61
-65
lines changed

17 files changed

+61
-65
lines changed

.editorconfig

+6
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,9 @@ charset = utf-8-bom
7272
[*.{cs,vb}]
7373
# CA1305: Specify IFormatProvider
7474
dotnet_diagnostic.CA1305.severity = error
75+
76+
[*.{cs,vb}]
77+
# CA2012: Use ValueTask correctly
78+
dotnet_diagnostic.CA2012.severity = warning
79+
[**/{test,perf}/**.{cs,vb}]
80+
dotnet_diagnostic.CA2012.severity = suggestion

src/Components/Components/src/Routing/Router.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ private void OnLocationChanged(object sender, LocationChangedEventArgs args)
267267
_locationAbsolute = args.Location;
268268
if (_renderHandle.IsInitialized && Routes != null)
269269
{
270-
_ = RunOnNavigateAsync(NavigationManager.ToBaseRelativePath(_locationAbsolute), args.IsNavigationIntercepted);
270+
_ = RunOnNavigateAsync(NavigationManager.ToBaseRelativePath(_locationAbsolute), args.IsNavigationIntercepted).Preserve();
271271
}
272272
}
273273

src/Components/Server/src/Circuits/RemoteNavigationManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected override void NavigateToCore(string uri, bool forceLoad)
7575
throw new NavigationException(absoluteUriString);
7676
}
7777

78-
_jsRuntime.InvokeAsync<object>(Interop.NavigateTo, uri, forceLoad);
78+
_jsRuntime.InvokeAsync<object>(Interop.NavigateTo, uri, forceLoad).Preserve();
7979
}
8080

8181
private static class Log

src/Hosting/Hosting/src/Internal/WebHost.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ public async Task StopAsync(CancellationToken cancellationToken = default)
334334

335335
public void Dispose()
336336
{
337-
DisposeAsync().GetAwaiter().GetResult();
337+
DisposeAsync().AsTask().GetAwaiter().GetResult();
338338
}
339339

340340
public async ValueTask DisposeAsync()

src/Http/Http/src/Features/RequestServicesFeature.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ static async ValueTask Awaited(RequestServicesFeature servicesFeature, ValueTask
8787
/// <inheritdoc />
8888
public void Dispose()
8989
{
90-
DisposeAsync().GetAwaiter().GetResult();
90+
DisposeAsync().AsTask().GetAwaiter().GetResult();
9191
}
9292
}
9393
}

src/Servers/Kestrel/Core/src/Internal/Http/Http1OutputProducer.cs

+15-22
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ internal class Http1OutputProducer : IHttpOutputProducer, IDisposable
5858

5959
private bool _autoChunk;
6060

61-
// We rely on the TimingPipeFlusher to give us ValueTasks that can be safely awaited multiple times.
6261
private bool _writeStreamSuffixCalled;
63-
private ValueTask<FlushResult> _writeStreamSuffixValueTask;
6462

6563
private int _advancedBytesForChunk;
6664
private Memory<byte> _currentChunkMemory;
@@ -118,31 +116,27 @@ public ValueTask<FlushResult> WriteDataToPipeAsync(ReadOnlySpan<byte> buffer, Ca
118116

119117
public ValueTask<FlushResult> WriteStreamSuffixAsync()
120118
{
119+
ValueTask<FlushResult> result = default;
120+
121121
lock (_contextLock)
122122
{
123-
if (_writeStreamSuffixCalled)
123+
if (!_writeStreamSuffixCalled)
124124
{
125-
// If WriteStreamSuffixAsync has already been called, no-op and return the previously returned ValueTask.
126-
return _writeStreamSuffixValueTask;
127-
}
125+
if (_autoChunk)
126+
{
127+
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
128+
result = WriteAsyncInternal(ref writer, EndChunkedResponseBytes);
129+
}
130+
else if (_unflushedBytes > 0)
131+
{
132+
result = FlushAsync();
133+
}
128134

129-
if (_autoChunk)
130-
{
131-
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
132-
_writeStreamSuffixValueTask = WriteAsyncInternal(ref writer, EndChunkedResponseBytes);
133-
}
134-
else if (_unflushedBytes > 0)
135-
{
136-
_writeStreamSuffixValueTask = FlushAsync();
137-
}
138-
else
139-
{
140-
_writeStreamSuffixValueTask = default;
135+
_writeStreamSuffixCalled = true;
141136
}
142-
143-
_writeStreamSuffixCalled = true;
144-
return _writeStreamSuffixValueTask;
145137
}
138+
139+
return result;
146140
}
147141

148142
public ValueTask<FlushResult> FlushAsync(CancellationToken cancellationToken = default)
@@ -533,7 +527,6 @@ public void Reset()
533527
_currentMemoryPrefixBytes = 0;
534528
_autoChunk = false;
535529
_writeStreamSuffixCalled = false;
536-
_writeStreamSuffixValueTask = default;
537530
_currentChunkMemoryUpdated = false;
538531
_startCalled = false;
539532
}

src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,15 @@ public void ProduceContinue()
917917
((IHeaderDictionary)HttpRequestHeaders).TryGetValue(HeaderNames.Expect, out var expect) &&
918918
(expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase))
919919
{
920-
Output.Write100ContinueAsync().GetAwaiter().GetResult();
920+
ValueTask<FlushResult> vt = Output.Write100ContinueAsync();
921+
if (vt.IsCompleted)
922+
{
923+
vt.GetAwaiter().GetResult();
924+
}
925+
else
926+
{
927+
vt.AsTask().GetAwaiter().GetResult();
928+
}
921929
}
922930
}
923931

@@ -1050,6 +1058,8 @@ private Task WriteSuffix()
10501058
return WriteSuffixAwaited(writeTask);
10511059
}
10521060

1061+
writeTask.GetAwaiter().GetResult();
1062+
10531063
_requestProcessingStatus = RequestProcessingStatus.ResponseCompleted;
10541064

10551065
if (_keepAlive)

src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamInputFlowControl.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void UpdateWindows(int bytes)
5757
if (streamWindowUpdateSize > 0)
5858
{
5959
// Writing with the FrameWriter should only fail if given a canceled token, so just fire and forget.
60-
_ = _frameWriter.WriteWindowUpdateAsync(StreamId, streamWindowUpdateSize);
60+
_ = _frameWriter.WriteWindowUpdateAsync(StreamId, streamWindowUpdateSize).Preserve();
6161
}
6262

6363
UpdateConnectionWindow(bytes);
@@ -90,7 +90,7 @@ private void UpdateConnectionWindow(int bytes)
9090
if (connectionWindowUpdateSize > 0)
9191
{
9292
// Writing with the FrameWriter should only fail if given a canceled token, so just fire and forget.
93-
_ = _frameWriter.WriteWindowUpdateAsync(0, connectionWindowUpdateSize);
93+
_ = _frameWriter.WriteWindowUpdateAsync(0, connectionWindowUpdateSize).Preserve();
9494
}
9595
}
9696
}

src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public void Abort(ConnectionAbortedException ex)
157157
{
158158
if (TryClose())
159159
{
160-
_frameWriter.WriteGoAwayAsync(int.MaxValue, Http2ErrorCode.INTERNAL_ERROR);
160+
_frameWriter.WriteGoAwayAsync(int.MaxValue, Http2ErrorCode.INTERNAL_ERROR).Preserve();
161161
}
162162

163163
_frameWriter.Abort(ex);
@@ -1214,7 +1214,7 @@ private void UpdateConnectionState()
12141214

12151215
if (_gracefulCloseInitiator == GracefulCloseInitiator.Server && _clientActiveStreamCount > 0)
12161216
{
1217-
_frameWriter.WriteGoAwayAsync(int.MaxValue, Http2ErrorCode.NO_ERROR);
1217+
_frameWriter.WriteGoAwayAsync(int.MaxValue, Http2ErrorCode.NO_ERROR).Preserve();
12181218
}
12191219
}
12201220

@@ -1224,7 +1224,7 @@ private void UpdateConnectionState()
12241224
{
12251225
if (TryClose())
12261226
{
1227-
_frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, Http2ErrorCode.NO_ERROR);
1227+
_frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, Http2ErrorCode.NO_ERROR).Preserve();
12281228
}
12291229
}
12301230
else

src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal class Http2OutputProducer : IHttpOutputProducer, IHttpOutputAborter, IV
4343
private bool _writerComplete;
4444

4545
// Internal for testing
46-
internal ValueTask _dataWriteProcessingTask;
46+
internal Task _dataWriteProcessingTask;
4747
internal bool _disposed;
4848

4949
/// <summary>The core logic for the IValueTaskSource implementation.</summary>
@@ -394,7 +394,7 @@ public void Reset()
394394
{
395395
}
396396

397-
private async ValueTask ProcessDataWrites()
397+
private async Task ProcessDataWrites()
398398
{
399399
// ProcessDataWrites runs for the lifetime of the Http2OutputProducer, and is designed to be reused by multiple streams.
400400
// When Http2OutputProducer is no longer used (e.g. a stream is aborted and will no longer be used, or the connection is closed)

src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public void CompleteStream(bool errored)
145145
if (!errored)
146146
{
147147
// Don't block on IO. This never faults.
148-
_ = _http2Output.WriteRstStreamAsync(Http2ErrorCode.NO_ERROR);
148+
_ = _http2Output.WriteRstStreamAsync(Http2ErrorCode.NO_ERROR).Preserve();
149149
}
150150
RequestBodyPipe.Writer.Complete();
151151
}
@@ -545,7 +545,7 @@ internal void ResetAndAbort(ConnectionAbortedException abortReason, Http2ErrorCo
545545

546546
DecrementActiveClientStreamCount();
547547
// Don't block on IO. This never faults.
548-
_ = _http2Output.WriteRstStreamAsync(error);
548+
_ = _http2Output.WriteRstStreamAsync(error).Preserve();
549549

550550
AbortCore(abortReason);
551551
}

src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public void Abort(ConnectionAbortedException ex, Http3ErrorCode errorCode)
180180
{
181181
if (TryClose())
182182
{
183-
SendGoAway(_highestOpenedStreamId);
183+
SendGoAway(_highestOpenedStreamId).Preserve();
184184
}
185185

186186
_errorCodeFeature.Error = (long)errorCode;
@@ -213,10 +213,10 @@ public void OnTimeout(TimeoutReason reason)
213213
switch (reason)
214214
{
215215
case TimeoutReason.KeepAlive:
216-
SendGoAway(_highestOpenedStreamId);
216+
SendGoAway(_highestOpenedStreamId).Preserve();
217217
break;
218218
case TimeoutReason.TimeoutFeature:
219-
SendGoAway(_highestOpenedStreamId);
219+
SendGoAway(_highestOpenedStreamId).Preserve();
220220
break;
221221
case TimeoutReason.RequestHeaders:
222222
case TimeoutReason.ReadDataRate:
@@ -396,7 +396,7 @@ private void UpdateConnectionState()
396396
if (_gracefulCloseInitiator == GracefulCloseInitiator.Server && activeRequestCount > 0)
397397
{
398398
// Go away with largest streamid to initiate graceful shutdown.
399-
SendGoAway(VariableLengthIntegerHelper.EightByteLimit);
399+
SendGoAway(VariableLengthIntegerHelper.EightByteLimit).Preserve();
400400
}
401401
}
402402

@@ -406,7 +406,7 @@ private void UpdateConnectionState()
406406
{
407407
if (TryClose())
408408
{
409-
SendGoAway(_highestOpenedStreamId);
409+
SendGoAway(_highestOpenedStreamId).Preserve();
410410
}
411411
}
412412
else

src/Servers/Kestrel/Core/src/Internal/Http3/Http3OutputProducer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public Http3OutputProducer(
5151
_pipeReader = pipe.Reader;
5252

5353
_flusher = new TimingPipeFlusher(_pipeWriter, timeoutControl: null, log);
54-
_dataWriteProcessingTask = ProcessDataWrites();
54+
_dataWriteProcessingTask = ProcessDataWrites().Preserve();
5555
}
5656

5757
public void Dispose()

src/Servers/Kestrel/Transport.Quic/src/Internal/QuicConnectionContext.cs

+4-11
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal class QuicConnectionContext : TransportMultiplexedConnection, IProtocol
1919
private readonly IQuicTrace _log;
2020
private readonly CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource();
2121

22-
private ValueTask _closeTask;
22+
private Task? _closeTask;
2323

2424
public long Error { get; set; }
2525

@@ -55,15 +55,8 @@ public override async ValueTask DisposeAsync()
5555
{
5656
try
5757
{
58-
if (_closeTask != default)
59-
{
60-
_closeTask = _connection.CloseAsync(errorCode: 0);
61-
await _closeTask;
62-
}
63-
else
64-
{
65-
await _closeTask;
66-
}
58+
_closeTask ??= _connection.CloseAsync(errorCode: 0).AsTask();
59+
await _closeTask;
6760
}
6861
catch (Exception ex)
6962
{
@@ -78,7 +71,7 @@ public override async ValueTask DisposeAsync()
7871
public override void Abort(ConnectionAbortedException abortReason)
7972
{
8073
// dedup calls to abort here.
81-
_closeTask = _connection.CloseAsync(errorCode: Error);
74+
_closeTask = _connection.CloseAsync(errorCode: Error).AsTask();
8275
}
8376

8477
public override async ValueTask<ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default)

src/Shared/ServerInfrastructure/DuplexPipeStream.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ public override void SetLength(long value)
7171

7272
public override int Read(byte[] buffer, int offset, int count)
7373
{
74-
// ValueTask uses .GetAwaiter().GetResult() if necessary
75-
// https://github.com/dotnet/corefx/blob/f9da3b4af08214764a51b2331f3595ffaf162abe/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs#L156
76-
return ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default).Result;
74+
ValueTask<int> vt = ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default);
75+
return vt.IsCompleted ?
76+
vt.Result :
77+
vt.AsTask().GetAwaiter().GetResult();
7778
}
7879

7980
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)

src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs

+1-8
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,7 @@ private async Task ProcessReceives()
168168

169169
_application.Output.Advance(bytesReceived);
170170

171-
var flushTask = _application.Output.FlushAsync();
172-
173-
if (!flushTask.IsCompleted)
174-
{
175-
await flushTask;
176-
}
177-
178-
var result = flushTask.GetAwaiter().GetResult();
171+
var result = await _application.Output.FlushAsync();
179172
if (result.IsCompleted)
180173
{
181174
// Pipe consumer is shut down, do we stop writing

src/SignalR/server/Core/src/HubConnectionContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ private void KeepAliveTick()
625625
// If the transport channel is full, this will fail, but that's OK because
626626
// adding a Ping message when the transport is full is unnecessary since the
627627
// transport is still in the process of sending frames.
628-
_ = TryWritePingAsync();
628+
_ = TryWritePingAsync().Preserve();
629629

630630
// We only update the timestamp here, because updating on each sent message is bad for performance
631631
// There can be a lot of sent messages per 15 seconds

0 commit comments

Comments
 (0)