public int ReadData(Span <byte> buffer, CancellationToken cancellationToken) { if (buffer.Length == 0) { return(0); } (bool wait, int bytesRead) = TryReadFromBuffer(buffer); if (wait) { // Synchronously block waiting for data to be produced. Debug.Assert(bytesRead == 0); GetWaiterTask(cancellationToken).GetAwaiter().GetResult(); CancellationHelper.ThrowIfCancellationRequested(cancellationToken); (wait, bytesRead) = TryReadFromBuffer(buffer); Debug.Assert(!wait); } if (bytesRead != 0) { ExtendWindow(bytesRead); _connection.ExtendWindow(bytesRead); } return(bytesRead); }
private async Task CompleteCopyToAsync(Task copyTask, HttpConnection connection, CancellationToken cancellationToken) { CancellationTokenRegistration ctr = connection.RegisterCancellation(cancellationToken); try { await copyTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } // If cancellation is requested and tears down the connection, it could cause the copy // to end early but think it ended successfully. So we prioritize cancellation in this // race condition, and if we find after the copy has completed that cancellation has // been requested, we assume the copy completed due to cancellation and throw. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); Finish(connection); }
private async Task GetCancelableWaiterTask(CancellationToken cancellationToken) { using (cancellationToken.UnsafeRegister(s => { var thisRef = (Http2Stream)s; bool signalWaiter; lock (thisRef.SyncObject) { signalWaiter = thisRef._hasWaiter; thisRef._hasWaiter = false; } if (signalWaiter) { // Wake up the wait. It will then immediately check whether cancellation was requested and throw if it was. thisRef._waitSource.SetResult(true); } }, this)) { await new ValueTask(this, _waitSource.Version).ConfigureAwait(false); } CancellationHelper.ThrowIfCancellationRequested(cancellationToken); }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); if (_connection == null || buffer.Length == 0) { // Response body fully consumed or the caller didn't ask for any data return(0); } ValueTask <int> readTask = _connection.ReadBufferedAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } #if MONOTOUCH_WATCH catch (Exception exc) { if (CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } throw; } #else catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } #endif finally { ctr.Dispose(); } } if (bytesRead == 0) { // A cancellation request may have caused the EOF. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // We cannot reuse this connection, so close it. _connection.Dispose(); _connection = null; return(0); } return(bytesRead); }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); HttpConnection?connection = _connection; if (connection == null || buffer.Length == 0) { // Response body fully consumed or the caller didn't ask for any data return(0); } ValueTask <int> readTask = connection.ReadAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } } if (bytesRead == 0) { // If cancellation is requested and tears down the connection, it could cause the read // to return 0, which would otherwise signal the end of the data, but that would lead // the caller to think that it actually received all of the data, rather than it ending // early due to cancellation. So we prioritize cancellation in this race condition, and // if we read 0 bytes and then find that cancellation has requested, we assume cancellation // was the cause and throw. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // We cannot reuse this connection, so close it. if (HttpTelemetry.IsEnabled) { LogRequestStop(); } _connection = null; connection.Dispose(); } return(bytesRead); }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); HttpConnection?connection = _connection; if (connection == null) { // Response body fully consumed return(0); } ValueTask <int> readTask = connection.ReadBufferedAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } } if (bytesRead == 0 && buffer.Length != 0) { // A cancellation request may have caused the EOF. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // We cannot reuse this connection, so close it. _connection = null; connection.Dispose(); } return(bytesRead); }
private ValueTask GetWaiterTask(CancellationToken cancellationToken) { // No locking is required here to access _waitSource. To be here, we've already updated _hasWaiter (while holding the lock) // to indicate that we would be creating this waiter, and at that point the only code that could be await'ing _waitSource or // Reset'ing it is this code here. It's possible for this to race with the _waitSource being completed, but that's ok and is // handled by _waitSource as one of its primary purposes. We can't assert _hasWaiter here, though, as once we released the // lock, a producer could have seen _hasWaiter as true and both set it to false and signaled _waitSource. // With HttpClient, the supplied cancellation token will always be cancelable, as HttpClient supplies a token that // will have cancellation requested if CancelPendingRequests is called (or when a non-infinite Timeout expires). // However, this could still be non-cancelable if HttpMessageInvoker was used, at which point this will only be // cancelable if the caller's token was cancelable. To avoid the extra allocation here in such a case, we make // this pay-for-play: if the token isn't cancelable, return a ValueTask wrapping this object directly, and only // if it is cancelable, then register for the cancellation callback, allocate a task for the asynchronously // completing case, etc. return(cancellationToken.CanBeCanceled ? new ValueTask(GetWaiterTaskCore()) : new ValueTask(this, _waitSource.Version)); async Task GetWaiterTaskCore() { using (cancellationToken.UnsafeRegister(s => { var thisRef = (Http2Stream)s; bool signalWaiter; lock (thisRef.SyncObject) { signalWaiter = thisRef._hasWaiter; thisRef._hasWaiter = false; } if (signalWaiter) { // Wake up the wait. It will then immediately check whether cancellation was requested and throw if it was. thisRef._waitSource.SetResult(true); } }, this)) { await new ValueTask(this, _waitSource.Version).ConfigureAwait(false); } CancellationHelper.ThrowIfCancellationRequested(cancellationToken); } }
private async Task GetWaiterTask(CancellationToken cancellationToken) { var vt = new ValueTask(this, _waitSource.Version).AsTask(); using (cancellationToken.Register(s => { Http2Stream stream = (Http2Stream)s; bool signalWaiter; lock (stream.SyncObject) { signalWaiter = stream._hasWaiter; stream._hasWaiter = false; } if (signalWaiter) { stream._waitSource.SetException(new OperationCanceledException()); } }, this)) { await vt.ConfigureAwait(false); } CancellationHelper.ThrowIfCancellationRequested(cancellationToken); }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); if (_connection == null || buffer.Length == 0) { // Response body fully consumed or the caller didn't ask for any data return(0); } Debug.Assert(_contentBytesRemaining > 0); if ((ulong)buffer.Length > _contentBytesRemaining) { buffer = buffer.Slice(0, (int)_contentBytesRemaining); } ValueTask <int> readTask = _connection.ReadAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } } if (bytesRead <= 0) { // A cancellation request may have caused the EOF. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // Unexpected end of response stream. throw new IOException(SR.Format(SR.net_http_invalid_response_premature_eof_bytecount, _contentBytesRemaining)); } Debug.Assert((ulong)bytesRead <= _contentBytesRemaining); _contentBytesRemaining -= (ulong)bytesRead; if (_contentBytesRemaining == 0) { // End of response body _connection.CompleteResponse(); _connection = null; } return(bytesRead); }