-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Remove dependency on pipe events in HttpConnection #11132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,11 +14,20 @@ internal sealed class RawStream : Stream | |
| { | ||
| private readonly PipeReader _input; | ||
| private readonly PipeWriter _output; | ||
| private readonly bool _throwOnCancelled; | ||
| private volatile bool _cancelCalled; | ||
|
|
||
| public RawStream(PipeReader input, PipeWriter output) | ||
| public RawStream(PipeReader input, PipeWriter output, bool throwOnCancelled = false) | ||
| { | ||
| _input = input; | ||
| _output = output; | ||
| _throwOnCancelled = throwOnCancelled; | ||
| } | ||
|
|
||
| public void CancelPendingRead() | ||
| { | ||
| _cancelCalled = true; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does CancelPendingRead need to be synchronized?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not for our usage.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't even think _cancelCalled needs to be volatile since CancelPendingRead acquires and releases the Pipe's |
||
| _input.CancelPendingRead(); | ||
| } | ||
|
|
||
| public override bool CanRead => true; | ||
|
|
@@ -61,17 +70,17 @@ public override int Read(byte[] buffer, int offset, int count) | |
| { | ||
| // ValueTask uses .GetAwaiter().GetResult() if necessary | ||
| // https://github.com/dotnet/corefx/blob/f9da3b4af08214764a51b2331f3595ffaf162abe/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs#L156 | ||
| return ReadAsyncInternal(new Memory<byte>(buffer, offset, count)).Result; | ||
| return ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default).Result; | ||
| } | ||
|
|
||
| public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | ||
| public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) | ||
| { | ||
| return ReadAsyncInternal(new Memory<byte>(buffer, offset, count)).AsTask(); | ||
| return ReadAsyncInternal(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask(); | ||
| } | ||
|
|
||
| public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default) | ||
| { | ||
| return ReadAsyncInternal(destination); | ||
| return ReadAsyncInternal(destination, cancellationToken); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we're flowing the cancellationToken through RawStream, do we have a regression test? |
||
| } | ||
|
|
||
| public override void Write(byte[] buffer, int offset, int count) | ||
|
|
@@ -105,14 +114,21 @@ public override Task FlushAsync(CancellationToken cancellationToken) | |
| return WriteAsync(null, 0, 0, cancellationToken); | ||
| } | ||
|
|
||
| private async ValueTask<int> ReadAsyncInternal(Memory<byte> destination) | ||
| private async ValueTask<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken) | ||
| { | ||
| while (true) | ||
| { | ||
| var result = await _input.ReadAsync(); | ||
| var result = await _input.ReadAsync(cancellationToken); | ||
| var readableBuffer = result.Buffer; | ||
| try | ||
| { | ||
| if (_throwOnCancelled && result.IsCanceled && _cancelCalled) | ||
| { | ||
| // Reset the bool | ||
| _cancelCalled = false; | ||
| throw new OperationCanceledException(); | ||
davidfowl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (!readableBuffer.IsEmpty) | ||
| { | ||
| // buffer.Count is int | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,8 @@ internal partial class LibuvConnection : TransportConnection | |
|
|
||
| private MemoryHandle _bufferHandle; | ||
| private Task _processingTask; | ||
| private readonly TaskCompletionSource<object> _waitForConnectionClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); | ||
| private bool _connectionClosed; | ||
|
|
||
| public LibuvConnection(UvStreamHandle socket, | ||
| ILibuvTrace log, | ||
|
|
@@ -135,21 +137,10 @@ private async Task StartCore() | |
| // We're done with the socket now | ||
| _socket.Dispose(); | ||
|
|
||
| // Fire the connection closed token and wait for it to complete | ||
| var waitForConnectionClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); | ||
| // Ensure this always fires | ||
| FireConnectionClosed(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're going to continue firing ConnectionClosed for libuv output errors, we should do the same for the socket transport.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean? Aren't we doing that? It always fires no?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm now thinking about making this more like the socket transport instead. The important thing is consistency. Instead of calling FireConnectionClosed here, just don't dispose _socket here. Instead, close the uv_stream_handle only after both the output loop completed and a) you've received a terminal read status or In the case of b) you'd have to make sure to still call FireConnectionClosed when you close the socket ofc. |
||
|
|
||
| ThreadPool.UnsafeQueueUserWorkItem(state => | ||
| { | ||
| (var connection, var tcs) = state; | ||
|
|
||
| connection.CancelConnectionClosedToken(); | ||
|
|
||
| tcs.TrySetResult(null); | ||
| }, | ||
| (this, waitForConnectionClosedTcs), | ||
| preferLocal: false); | ||
|
|
||
| await waitForConnectionClosedTcs.Task; | ||
| await _waitForConnectionClosedTcs.Task; | ||
| } | ||
| } | ||
| catch (Exception e) | ||
|
|
@@ -241,11 +232,33 @@ private void OnRead(UvStreamHandle handle, int status) | |
| error = LogAndWrapReadError(uvError); | ||
| } | ||
|
|
||
| FireConnectionClosed(); | ||
|
|
||
| // Complete after aborting the connection | ||
| Input.Complete(error); | ||
| } | ||
| } | ||
|
|
||
| private void FireConnectionClosed() | ||
| { | ||
| // Guard against scheduling this multiple times | ||
| if (_connectionClosed) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see you're taking advantage of the single-threaded nature of libuv's event loop 👍 |
||
| { | ||
| return; | ||
| } | ||
|
|
||
| _connectionClosed = true; | ||
|
|
||
| ThreadPool.UnsafeQueueUserWorkItem(state => | ||
| { | ||
| state.CancelConnectionClosedToken(); | ||
|
|
||
| state._waitForConnectionClosedTcs.TrySetResult(null); | ||
| }, | ||
| this, | ||
| preferLocal: false); | ||
| } | ||
|
|
||
| private async Task ApplyBackpressureAsync(ValueTask<FlushResult> flushTask) | ||
| { | ||
| Log.ConnectionPause(ConnectionId); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.