/// <summary>Notifies the stream that no more data will be written.</summary> internal void SignalComplete(Exception error = null, bool forceCancel = false) { lock (_lockObject) { VerifyInvariants(); // If we already completed, nothing more to do if (_completed != null) { EventSourceTrace("Already completed"); return; } // Mark ourselves as being completed EventSourceTrace("Marking as complete"); _completed = error ?? s_completionSentinel; // If the request wasn't already completed, and if requested, send a cancellation // request to ensure that the connection gets cleaned up. This is only necessary // to do if this method is being called for a reason other than the request/response // completing naturally, e.g. if the response stream is being disposed of before // all of the response has been downloaded. if (forceCancel) { EventSourceTrace("Completing the response stream prematurely."); _easy._associatedMultiAgent.RequestCancel(_easy); } // If there's a pending read request, complete it, either with 0 bytes for success // or with the exception/CancellationToken for failure. ReadState pendingRead = _pendingReadRequest; if (pendingRead != null) { if (_completed == s_completionSentinel) { EventSourceTrace("Completing pending read with 0 bytes"); pendingRead.TrySetResult(0); } else { EventSourceTrace("Failing pending read task with error: {0}", _completed); OperationCanceledException oce = _completed as OperationCanceledException; if (oce != null) { pendingRead.TrySetCanceled(oce.CancellationToken); } else { pendingRead.TrySetException(MapToReadWriteIOException(_completed, isRead: true)); } } ClearPendingReadRequest(); } } }