Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit 7e71cdd

Browse files
author
Geoff Kizer
committed
rework async send handling to avoid "async void"
1 parent 0228f94 commit 7e71cdd

File tree

2 files changed

+71
-21
lines changed

2 files changed

+71
-21
lines changed

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs

+69-20
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,19 @@ private async Task FlushOutgoingBytesAsync()
108108
{
109109
Debug.Assert(_outgoingBuffer.ActiveSpan.Length > 0);
110110

111-
await _stream.WriteAsync(_outgoingBuffer.ActiveMemory).ConfigureAwait(false);
112-
_outgoingBuffer.Discard(_outgoingBuffer.ActiveMemory.Length);
111+
try
112+
{
113+
await _stream.WriteAsync(_outgoingBuffer.ActiveMemory).ConfigureAwait(false);
114+
}
115+
catch (Exception)
116+
{
117+
Abort();
118+
throw;
119+
}
120+
finally
121+
{
122+
_outgoingBuffer.Discard(_outgoingBuffer.ActiveMemory.Length);
123+
}
113124
}
114125

115126
private async Task<FrameHeader> ReadFrameAsync()
@@ -181,7 +192,7 @@ private async void ProcessIncomingFrames()
181192
}
182193
catch (Exception)
183194
{
184-
AbortStreams(0);
195+
Abort();
185196
}
186197
}
187198

@@ -216,7 +227,9 @@ private async Task ProcessHeadersFrame(FrameHeader frameHeader)
216227
if (http2Stream == null)
217228
{
218229
_incomingBuffer.Discard(frameHeader.Length);
219-
SendRstStream(streamId, Http2ProtocolErrorCode.StreamClosed);
230+
231+
// Don't wait for completion, which could happen asynchronously.
232+
ValueTask ignored = SendRstStreamAsync(streamId, Http2ProtocolErrorCode.StreamClosed);
220233
return;
221234
}
222235

@@ -292,7 +305,9 @@ private void ProcessDataFrame(FrameHeader frameHeader)
292305
if (http2Stream == null)
293306
{
294307
_incomingBuffer.Discard(frameHeader.Length);
295-
SendRstStream(frameHeader.StreamId, Http2ProtocolErrorCode.StreamClosed);
308+
309+
// Don't wait for completion, which could happen asynchronously.
310+
ValueTask ignored = SendRstStreamAsync(frameHeader.StreamId, Http2ProtocolErrorCode.StreamClosed);
296311
return;
297312
}
298313

@@ -349,7 +364,8 @@ private void ProcessSettingsFrame(FrameHeader frameHeader)
349364
_incomingBuffer.Discard(frameHeader.Length);
350365

351366
// Send acknowledgement
352-
SendSettingsAck();
367+
// Don't wait for completion, which could happen asynchronously.
368+
ValueTask ignored = SendSettingsAckAsync();
353369
}
354370
}
355371

@@ -388,7 +404,8 @@ private void ProcessPingFrame(FrameHeader frameHeader)
388404
}
389405

390406
// Send PING ACK
391-
SendPingAck(_incomingBuffer.ActiveMemory.Slice(0, FrameHeader.PingLength));
407+
// Don't wait for completion, which could happen asynchronously.
408+
ValueTask ignored = SendPingAckAsync(_incomingBuffer.ActiveMemory.Slice(0, FrameHeader.PingLength));
392409

393410
_incomingBuffer.Discard(frameHeader.Length);
394411
}
@@ -461,9 +478,29 @@ private void ProcessGoAwayFrame(FrameHeader frameHeader)
461478
_incomingBuffer.Discard(frameHeader.Length);
462479
}
463480

464-
private async void SendSettingsAck()
481+
private async ValueTask AcquireWriteLockAsync()
465482
{
466483
await _writerLock.WaitAsync().ConfigureAwait(false);
484+
485+
// If the connection has been aborted, then fail now instead of trying to send more data.
486+
if (IsAborted())
487+
{
488+
throw new IOException(SR.net_http_invalid_response);
489+
}
490+
}
491+
492+
private void ReleaseWriteLock()
493+
{
494+
// Currently, we always flush the write buffer before releasing the lock.
495+
// If we change this in the future, we will need to revisit this assert.
496+
Debug.Assert(_outgoingBuffer.ActiveMemory.IsEmpty);
497+
498+
_writerLock.Release();
499+
}
500+
501+
private async ValueTask SendSettingsAckAsync()
502+
{
503+
await AcquireWriteLockAsync().ConfigureAwait(false);
467504
try
468505
{
469506
WriteFrameHeader(new FrameHeader(0, FrameType.Settings, FrameFlags.Ack, 0));
@@ -472,15 +509,15 @@ private async void SendSettingsAck()
472509
}
473510
finally
474511
{
475-
_writerLock.Release();
512+
ReleaseWriteLock();
476513
}
477514
}
478515

479-
private async void SendPingAck(ReadOnlyMemory<byte> pingContent)
516+
private async ValueTask SendPingAckAsync(ReadOnlyMemory<byte> pingContent)
480517
{
481518
Debug.Assert(pingContent.Length == FrameHeader.PingLength);
482519

483-
await _writerLock.WaitAsync().ConfigureAwait(false);
520+
await AcquireWriteLockAsync().ConfigureAwait(false);
484521
try
485522
{
486523
WriteFrameHeader(new FrameHeader(FrameHeader.PingLength, FrameType.Ping, FrameFlags.Ack, 0));
@@ -492,13 +529,13 @@ private async void SendPingAck(ReadOnlyMemory<byte> pingContent)
492529
}
493530
finally
494531
{
495-
_writerLock.Release();
532+
ReleaseWriteLock();
496533
}
497534
}
498535

499-
private async void SendRstStream(int streamId, Http2ProtocolErrorCode errorCode)
536+
private async ValueTask SendRstStreamAsync(int streamId, Http2ProtocolErrorCode errorCode)
500537
{
501-
await _writerLock.WaitAsync().ConfigureAwait(false);
538+
await AcquireWriteLockAsync().ConfigureAwait(false);
502539
try
503540
{
504541
WriteFrameHeader(new FrameHeader(FrameHeader.RstStreamLength, FrameType.RstStream, FrameFlags.None, streamId));
@@ -514,7 +551,7 @@ private async void SendRstStream(int streamId, Http2ProtocolErrorCode errorCode)
514551
}
515552
finally
516553
{
517-
_writerLock.Release();
554+
ReleaseWriteLock();
518555
}
519556
}
520557

@@ -532,7 +569,7 @@ private async ValueTask SendStreamDataAsync(int streamId, ReadOnlyMemory<byte> b
532569
ReadOnlyMemory<byte> current;
533570
(current, remaining) = SplitBufferForFraming(remaining);
534571

535-
await _writerLock.WaitAsync().ConfigureAwait(false);
572+
await AcquireWriteLockAsync().ConfigureAwait(false);
536573
try
537574
{
538575
_outgoingBuffer.EnsureAvailableSpace(FrameHeader.Size + current.Length);
@@ -544,14 +581,14 @@ private async ValueTask SendStreamDataAsync(int streamId, ReadOnlyMemory<byte> b
544581
}
545582
finally
546583
{
547-
_writerLock.Release();
584+
ReleaseWriteLock();
548585
}
549586
}
550587
}
551588

552-
private async void SendEndStream(int streamId)
589+
private async ValueTask SendEndStreamAsync(int streamId)
553590
{
554-
await _writerLock.WaitAsync().ConfigureAwait(false);
591+
await AcquireWriteLockAsync().ConfigureAwait(false);
555592
try
556593
{
557594
_outgoingBuffer.EnsureAvailableSpace(FrameHeader.Size);
@@ -560,7 +597,7 @@ private async void SendEndStream(int streamId)
560597
}
561598
finally
562599
{
563-
_writerLock.Release();
600+
ReleaseWriteLock();
564601
}
565602
}
566603

@@ -571,6 +608,18 @@ private void WriteFrameHeader(FrameHeader frameHeader)
571608
_outgoingBuffer.Commit(FrameHeader.Size);
572609
}
573610

611+
private void Abort()
612+
{
613+
// The connection has failed, e.g. failed IO or a connection-level frame error.
614+
// Abort all streams and cause further processing to fail.
615+
AbortStreams(0);
616+
}
617+
618+
private bool IsAborted()
619+
{
620+
return _disposed;
621+
}
622+
574623
private void AbortStreams(int lastValidStream)
575624
{
576625
lock (_syncObject)

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@ protected override void Dispose(bool disposing)
543543
return;
544544
}
545545

546-
_http2Stream._connection.SendEndStream(_http2Stream.StreamId);
546+
// Don't wait for completion, which could happen asynchronously.
547+
ValueTask ignored = _http2Stream._connection.SendEndStreamAsync(_http2Stream.StreamId);
547548

548549
base.Dispose(disposing);
549550
}

0 commit comments

Comments
 (0)