diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs index d5f1c11c4b..962e8046fc 100644 --- a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs @@ -56,7 +56,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerOpenDelegate( _nativeObjPtr, - MsQuicListener.NativeCallbackHandler, + MsQuicListener.s_listenerDelegate, IntPtr.Zero, out IntPtr listenerPointer), "Could not open listener."); diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs index 708e15ee2c..632c755294 100644 --- a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -37,8 +37,8 @@ namespace System.Net.Quic.Implementations.MsQuic private SslApplicationProtocol _negotiatedAlpnProtocol; // TODO: only allocate these when there is an outstanding connect/shutdown. - private readonly TaskCompletionSource _connectTcs = new TaskCompletionSource(); - private readonly TaskCompletionSource _shutdownTcs = new TaskCompletionSource(); + private readonly TaskCompletionSource _connectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly TaskCompletionSource _shutdownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); private bool _disposed; private bool _connected; @@ -291,6 +291,8 @@ namespace System.Net.Quic.Implementations.MsQuic ErrorCode); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection."); + Debug.Assert(_shutdownTcs.Task.IsCompleted == false); + return new ValueTask(_shutdownTcs.Task); } @@ -349,6 +351,8 @@ namespace System.Net.Quic.Implementations.MsQuic _disposed = true; } + // TODO: this appears abortive and will cause prior successfully shutdown and closed streams to drop data. + // It's unclear how to gracefully wait for a connection to be 100% done. internal override ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default) { ThrowIfDisposed(); diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs index 721a8e554e..4e83c8613d 100644 --- a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -26,7 +26,7 @@ namespace System.Net.Quic.Implementations.MsQuic private GCHandle _handle; // Delegate that wraps the static function that will be called when receiving an event. - private static readonly ListenerCallbackDelegate s_listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); + internal static readonly ListenerCallbackDelegate s_listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); // Ssl listening options (ALPN, cert, etc) private readonly SslServerAuthenticationOptions _sslOptions; @@ -189,7 +189,7 @@ namespace System.Net.Quic.Implementations.MsQuic _acceptConnectionQueue.Writer.TryComplete(); } - internal static uint NativeCallbackHandler( + private static uint NativeCallbackHandler( IntPtr listener, IntPtr context, ref ListenerEvent connectionEventStruct) diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs index be0e4a8d03..48a135799b 100644 --- a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -34,7 +34,8 @@ namespace System.Net.Quic.Implementations.MsQuic // Resettable completions to be used for multiple calls to receive. private readonly ResettableCompletionSource _receiveResettableCompletionSource; - private readonly ResettableCompletionSource _shutdownWriteResettableCompletionSource; + // Set once writes have been shutdown. + private readonly TaskCompletionSource _shutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); // Buffers to hold during a call to send. private MemoryHandle[] _bufferArrays = new MemoryHandle[1]; @@ -49,7 +50,7 @@ namespace System.Net.Quic.Implementations.MsQuic private ReadState _readState; private long _readErrorCode = -1; - private ShutdownWriteState _shutdownState; + private ShutdownWriteState _shutdownWriteState; private SendState _sendState; private long _sendErrorCode = -1; @@ -76,7 +77,6 @@ namespace System.Net.Quic.Implementations.MsQuic _sendResettableCompletionSource = new ResettableCompletionSource(); _receiveResettableCompletionSource = new ResettableCompletionSource(); - _shutdownWriteResettableCompletionSource = new ResettableCompletionSource(); SetCallbackHandler(); bool isBidirectional = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); @@ -323,16 +323,16 @@ namespace System.Net.Quic.Implementations.MsQuic lock (_sync) { - if (_shutdownState == ShutdownWriteState.None) + if (_shutdownWriteState == ShutdownWriteState.None) { - _shutdownState = ShutdownWriteState.Canceled; + _shutdownWriteState = ShutdownWriteState.Canceled; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteResettableCompletionSource.CompleteException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); + _shutdownWriteCompletionSource.SetException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); } MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_SEND, errorCode); @@ -348,20 +348,20 @@ namespace System.Net.Quic.Implementations.MsQuic bool shouldComplete = false; lock (_sync) { - if (_shutdownState == ShutdownWriteState.None) + if (_shutdownWriteState == ShutdownWriteState.None) { - _shutdownState = ShutdownWriteState.Canceled; + _shutdownWriteState = ShutdownWriteState.Canceled; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteResettableCompletionSource.CompleteException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); + _shutdownWriteCompletionSource.SetException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); } }); - return _shutdownWriteResettableCompletionSource.GetTypelessValueTask(); + return new ValueTask(_shutdownWriteCompletionSource.Task); } internal override void Shutdown() @@ -408,8 +408,6 @@ namespace System.Net.Quic.Implementations.MsQuic return default; } - CleanupSendState(); - if (_ptr != IntPtr.Zero) { // TODO resolve graceful vs abortive dispose here. Will file a separate issue. @@ -419,6 +417,8 @@ namespace System.Net.Quic.Implementations.MsQuic _handle.Free(); + CleanupSendState(); + _disposed = true; return default; @@ -442,8 +442,6 @@ namespace System.Net.Quic.Implementations.MsQuic return; } - CleanupSendState(); - if (_ptr != IntPtr.Zero) { // TODO resolve graceful vs abortive dispose here. Will file a separate issue. @@ -453,6 +451,8 @@ namespace System.Net.Quic.Implementations.MsQuic _handle.Free(); + CleanupSendState(); + _disposed = true; } @@ -461,7 +461,7 @@ namespace System.Net.Quic.Implementations.MsQuic MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_ptr, enabled: true); } - internal static uint NativeCallbackHandler( + private static uint NativeCallbackHandler( IntPtr stream, IntPtr context, ref StreamEvent streamEvent) @@ -614,16 +614,16 @@ namespace System.Net.Quic.Implementations.MsQuic bool shouldComplete = false; lock (_sync) { - if (_shutdownState == ShutdownWriteState.None) + if (_shutdownWriteState == ShutdownWriteState.None) { - _shutdownState = ShutdownWriteState.Finished; + _shutdownWriteState = ShutdownWriteState.Finished; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + _shutdownWriteCompletionSource.TrySetResult(); } return MsQuicStatusCodes.Success; @@ -646,9 +646,9 @@ namespace System.Net.Quic.Implementations.MsQuic _readState = ReadState.ReadsCompleted; - if (_shutdownState == ShutdownWriteState.None) + if (_shutdownWriteState == ShutdownWriteState.None) { - _shutdownState = ShutdownWriteState.Finished; + _shutdownWriteState = ShutdownWriteState.Finished; shouldShutdownWriteComplete = true; } } @@ -660,7 +660,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (shouldShutdownWriteComplete) { - _shutdownWriteResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + _shutdownWriteCompletionSource.TrySetResult(); } return MsQuicStatusCodes.Success;