internal override async ValueTask <int> ReadAsync(Memory <byte> destination, CancellationToken cancellationToken = default) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } ThrowIfDisposed(); if (!_canRead) { throw new InvalidOperationException("Reading is not allowed on stream."); } lock (_sync) { if (_readState == ReadState.ReadsCompleted) { if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } return(0); } else if (_readState == ReadState.Aborted) { throw _readErrorCode switch { -1 => new QuicOperationAbortedException(), long err => new QuicStreamAbortedException(err) }; } } using CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; lock (_sync) { if (_readState == ReadState.None) { shouldComplete = true; } _readState = ReadState.Aborted; } if (shouldComplete) { _receiveResettableCompletionSource.CompleteException(new OperationCanceledException("Read was canceled", cancellationToken)); } }); // TODO there could potentially be a perf gain by storing the buffer from the inital read // This reduces the amount of async calls, however it makes it so MsQuic holds onto the buffers // longer than it needs to. We will need to benchmark this. int length = (int)await _receiveResettableCompletionSource.GetValueTask().ConfigureAwait(false); int actual = Math.Min(length, destination.Length);
private uint HandleEventShutdownInitiatedByTransport(ConnectionEvent connectionEvent) { if (!_connected) { _connectTcs.CompleteException(new IOException("Connection has been shutdown.")); } _acceptQueue.Writer.Complete(); return(MsQuicStatusCodes.Success); }
private async ValueTask <CancellationTokenRegistration> HandleWriteStartState(CancellationToken cancellationToken) { if (!_canWrite) { throw new InvalidOperationException("Writing is not allowed on stream."); } lock (_sync) { if (_sendState == SendState.Aborted) { throw new OperationCanceledException("Sending has already been aborted on the stream"); } } CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; lock (_sync) { if (_sendState == SendState.None) { _sendState = SendState.Aborted; shouldComplete = true; } } if (shouldComplete) { _sendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled", cancellationToken)); } }); // Make sure start has completed if (!_started) { await _sendResettableCompletionSource.GetTypelessValueTask().ConfigureAwait(false); _started = true; } return(registration); }
private async ValueTask <CancellationTokenRegistration> HandleWriteStartState(CancellationToken cancellationToken) { if (!_canWrite) { throw new InvalidOperationException("Writing is not allowed on stream."); } lock (_sync) { if (_sendState == SendState.Aborted) { throw new OperationCanceledException("Sending has already been aborted on the stream"); } } CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; lock (_sync) { if (_sendState == SendState.None) { _sendState = SendState.Aborted; shouldComplete = true; } } if (shouldComplete) { _sendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled")); } }); // Implicit start on first write. if (_started == StartState.None) { _started = StartState.Started; // TODO can optimize this by not having this method be async. await StartWritesAsync(); } return(registration); }
private uint HandleEventShutdownInitiatedByTransport(ConnectionEvent connectionEvent) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } if (!_connected) { _connectTcs.CompleteException(new IOException("Connection has been shutdown.")); } _acceptQueue.Writer.Complete(); if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } return(MsQuicStatusCodes.Success); }
internal override async ValueTask WriteAsync(ReadOnlyMemory <byte> buffer, bool endStream, CancellationToken cancellationToken = default) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } ThrowIfDisposed(); if (!_canWrite) { throw new InvalidOperationException("Writing is not allowed on stream."); } lock (_sync) { if (_sendState == SendState.Aborted) { throw new OperationCanceledException("Sending has already been aborted on the stream"); } } using CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; lock (_sync) { if (_sendState == SendState.None) { _sendState = SendState.Aborted; shouldComplete = true; } } if (shouldComplete) { _sendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled")); } }); // Implicit start on first write. if (_started == StartState.None) { _started = StartState.Started; await StartWritesAsync(); } await SendAsync(buffer, endStream?QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE); lock (_sync) { if (_sendState == SendState.Finished) { _sendState = SendState.None; } } if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } }