Ejemplo n.º 1
0
            public override Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            {
                if (buffer == null)
                {
                    throw new ArgumentNullException("buffer");
                }
                if (offset < 0)
                {
                    throw new ArgumentOutOfRangeException("offset");
                }
                if (count < 0)
                {
                    throw new ArgumentOutOfRangeException("count");
                }
                if (offset > buffer.Length - count)
                {
                    throw new ArgumentException("buffer");
                }
                CheckDisposed();

                VerboseTrace("buffer: " + buffer.Length + ", offset: " + offset + ", count: " + count);

                // Check for cancellation
                if (cancellationToken.IsCancellationRequested)
                {
                    VerboseTrace("Canceled");
                    return(Task.FromCanceled <int>(cancellationToken));
                }

                lock (_lockObject)
                {
                    VerifyInvariants();

                    // If there's currently a pending read, fail this read, as we don't support concurrent reads.
                    if (_pendingReadRequest != null)
                    {
                        VerboseTrace("Existing pending read");
                        return(Task.FromException <int>(new InvalidOperationException(SR.net_http_content_no_concurrent_reads)));
                    }

                    // If the stream was already completed with failure, complete the read as a failure.
                    if (_completed != null && _completed != s_completionSentinel)
                    {
                        VerboseTrace("Failing read with " + _completed);
                        OperationCanceledException oce = _completed as OperationCanceledException;
                        return((oce != null && oce.CancellationToken.IsCancellationRequested) ?
                               Task.FromCanceled <int>(oce.CancellationToken) :
                               Task.FromException <int>(_completed));
                    }

                    // Quick check for if no data was actually requested.  We do this after the check
                    // for errors so that we can still fail the read and transfer the exception if we should.
                    if (count == 0)
                    {
                        VerboseTrace("Zero count");
                        return(s_zeroTask);
                    }

                    // If there's any data left over from a previous call, grab as much as we can.
                    if (_remainingDataCount > 0)
                    {
                        int bytesToCopy = Math.Min(count, _remainingDataCount);
                        Array.Copy(_remainingData, _remainingDataOffset, buffer, offset, bytesToCopy);

                        _remainingDataOffset += bytesToCopy;
                        _remainingDataCount  -= bytesToCopy;
                        Debug.Assert(_remainingDataCount >= 0, "The remaining count should never go negative");
                        Debug.Assert(_remainingDataOffset <= _remainingData.Length, "The remaining offset should never exceed the buffer size");

                        VerboseTrace("Copied to task: " + bytesToCopy);
                        return(Task.FromResult(bytesToCopy));
                    }

                    // If the stream has already been completed, complete the read immediately.
                    if (_completed == s_completionSentinel)
                    {
                        VerboseTrace("Completed successfully after stream completion");
                        return(s_zeroTask);
                    }

                    // Finally, the stream is still alive, and we want to read some data, but there's no data
                    // in the buffer so we need to register ourself to get the next write.
                    if (cancellationToken.CanBeCanceled)
                    {
                        // If the cancellation token is cancelable, then we need to register for cancellation.
                        // We creat a special CancelableReadState that carries with it additional info:
                        // the cancellation token and the registration with that token.  When cancellation
                        // is requested, we schedule a work item that tries to remove the read state
                        // from being pending, canceling it in the process.  This needs to happen under the
                        // lock, which is why we schedule the operation to run asynchronously: if it ran
                        // synchronously, it could deadlock due to code on another thread holding the lock
                        // and calling Dispose on the registration concurrently with the call to Cancel
                        // the cancellation token.  Dispose on the registration won't return until the action
                        // associated with the registration has completed, but if that action is currently
                        // executing and is blocked on the lock that's held while calling Dispose... deadlock.
                        var crs = new CancelableReadState(buffer, offset, count, this, cancellationToken);
                        crs._registration = cancellationToken.Register(s1 =>
                        {
                            ((CancelableReadState)s1)._stream.VerboseTrace("Cancellation invoked. Queueing work item to cancel read state.");
                            Task.Factory.StartNew(s2 =>
                            {
                                var crsRef = (CancelableReadState)s2;
                                Debug.Assert(crsRef._token.IsCancellationRequested, "We should only be here if cancellation was requested.");
                                lock (crsRef._stream._lockObject)
                                {
                                    if (crsRef._stream._pendingReadRequest == crsRef)
                                    {
                                        crsRef.TrySetCanceled(crsRef._token);
                                        crsRef._stream.ClearPendingReadRequest();
                                    }
                                }
                            }, s1, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
                        }, crs);
                        _pendingReadRequest = crs;
                        VerboseTrace("Created pending cancelable read");
                    }
                    else
                    {
                        // The token isn't cancelable.  Just create a normal read state.
                        _pendingReadRequest = new ReadState(buffer, offset, count);
                        VerboseTrace("Created pending read");
                    }

                    _easy._associatedMultiAgent.RequestUnpause(_easy);
                    return(_pendingReadRequest.Task);
                }
            }
Ejemplo n.º 2
0
            public override ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken = default)
            {
                CheckDisposed();

                EventSourceTrace("Buffer: {0}", buffer.Length);

                // Check for cancellation
                if (cancellationToken.IsCancellationRequested)
                {
                    EventSourceTrace("Canceled");
                    return(new ValueTask <int>(Task.FromCanceled <int>(cancellationToken)));
                }

                lock (_lockObject)
                {
                    VerifyInvariants();

                    // If there's currently a pending read, fail this read, as we don't support concurrent reads.
                    if (_pendingReadRequest != null)
                    {
                        EventSourceTrace("Failing due to existing pending read; concurrent reads not supported.");
                        return(new ValueTask <int>(Task.FromException <int>(new InvalidOperationException(SR.net_http_content_no_concurrent_reads))));
                    }

                    // If the stream was already completed with failure, complete the read as a failure.
                    if (_completed != null && _completed != s_completionSentinel)
                    {
                        EventSourceTrace("Failing read with error: {0}", _completed);

                        OperationCanceledException oce = _completed as OperationCanceledException;
                        return(new ValueTask <int>((oce != null && oce.CancellationToken.IsCancellationRequested) ?
                                                   Task.FromCanceled <int>(oce.CancellationToken) :
                                                   Task.FromException <int>(MapToReadWriteIOException(_completed, isRead: true))));
                    }

                    // Quick check for if no data was actually requested.  We do this after the check
                    // for errors so that we can still fail the read and transfer the exception if we should.
                    if (buffer.Length == 0)
                    {
                        return(new ValueTask <int>(0));
                    }

                    // If there's any data left over from a previous call, grab as much as we can.
                    if (_remainingDataCount > 0)
                    {
                        int bytesToCopy = Math.Min(buffer.Length, _remainingDataCount);
                        new Span <byte>(_remainingData, _remainingDataOffset, bytesToCopy).CopyTo(buffer.Span);

                        _remainingDataOffset += bytesToCopy;
                        _remainingDataCount  -= bytesToCopy;
                        Debug.Assert(_remainingDataCount >= 0, "The remaining count should never go negative");
                        Debug.Assert(_remainingDataOffset <= _remainingData.Length, "The remaining offset should never exceed the buffer size");

                        EventSourceTrace("Read {0} bytes", bytesToCopy);
                        return(new ValueTask <int>(bytesToCopy));
                    }

                    // If the stream has already been completed, complete the read immediately.
                    if (_completed == s_completionSentinel)
                    {
                        EventSourceTrace("Stream already completed");
                        return(new ValueTask <int>(0));
                    }

                    // Finally, the stream is still alive, and we want to read some data, but there's no data
                    // in the buffer so we need to register ourself to get the next write.
                    if (cancellationToken.CanBeCanceled)
                    {
                        // If the cancellation token is cancelable, then we need to register for cancellation.
                        // We create a special CancelableReadState that carries with it additional info:
                        // the cancellation token and the registration with that token.  When cancellation
                        // is requested, we schedule a work item that tries to remove the read state
                        // from being pending, canceling it in the process.  This needs to happen under the
                        // lock, which is why we schedule the operation to run asynchronously: if it ran
                        // synchronously, it could deadlock due to code on another thread holding the lock
                        // and calling Dispose on the registration concurrently with the call to Cancel
                        // the cancellation token.  Dispose on the registration won't return until the action
                        // associated with the registration has completed, but if that action is currently
                        // executing and is blocked on the lock that's held while calling Dispose... deadlock.
                        var crs = new CancelableReadState(buffer, this, cancellationToken);
                        crs._registration = cancellationToken.Register(s1 =>
                        {
                            ((CancelableReadState)s1)._stream.EventSourceTrace("Cancellation invoked. Queueing work item to cancel read state");
                            Task.Factory.StartNew(s2 =>
                            {
                                var crsRef = (CancelableReadState)s2;
                                lock (crsRef._stream._lockObject)
                                {
                                    Debug.Assert(crsRef._token.IsCancellationRequested, "We should only be here if cancellation was requested.");
                                    if (crsRef._stream._pendingReadRequest == crsRef)
                                    {
                                        crsRef._stream.EventSourceTrace("Canceling");
                                        crsRef.TrySetCanceled(crsRef._token);
                                        crsRef._stream.ClearPendingReadRequest();
                                    }
                                }
                            }, s1, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
                        }, crs);
                        _pendingReadRequest = crs;
                    }
                    else
                    {
                        // The token isn't cancelable.  Just create a normal read state.
                        _pendingReadRequest = new ReadState(buffer);
                    }

                    _easy._associatedMultiAgent.RequestUnpause(_easy);
                    _easy._selfStrongToWeakReference.MakeStrong(); // convert from a weak to a strong ref to keep the easy alive during the read
                    return(new ValueTask <int>(_pendingReadRequest.Task));
                }
            }