private static async Task FlushAsyncInternal(CancellationToken cancellationToken, BufferedStream _this, Stream stream, Int32 writePos, Int32 readPos, Int32 readLen) { // We bring instance fields down as local parameters to this async method becasue BufferedStream is derived from MarshalByRefObject. // Field access would be from the async state machine i.e., not via the this pointer and would require runtime checking to see // if we are talking to a remote object, which is currently very slow Contract.Assert(stream != null); SemaphoreSlim sem = _this.EnsureAsyncActiveSemaphoreInitialized(); await sem.WaitAsync().ConfigureAwait(false); try { if (writePos > 0) { await _this.FlushWriteAsync(cancellationToken).ConfigureAwait(false); Contract.Assert(_this._writePos == 0 && _this._readPos == 0 && _this._readLen == 0); return; } if (readPos < readLen) { // If the underlying stream is not seekable AND we have something in the read buffer, then FlushRead would throw. // We can either throw away the buffer resulting in date loss (!) or ignore the Flush. (We cannot throw becasue it // would be a breaking change.) We opt into ignoring the Flush in that situation. if (!stream.CanSeek) return; _this.FlushRead(); // not async; it uses Seek, but there's no SeekAsync // User streams may have opted to throw from Flush if CanWrite is false (although the abstract Stream does not do so). // However, if we do not forward the Flush to the underlying stream, we may have problems when chaining several streams. // Let us make a best effort attempt: if (stream.CanRead || stream is BufferedStream) await stream.FlushAsync(cancellationToken).ConfigureAwait(false); Contract.Assert(_this._writePos == 0 && _this._readPos == 0 && _this._readLen == 0); return; } // We had no data in the buffer, but we still need to tell the underlying stream to flush. if (stream.CanWrite || stream is BufferedStream) await stream.FlushAsync(cancellationToken).ConfigureAwait(false); // There was nothing in the buffer: Contract.Assert(_this._writePos == 0 && _this._readPos == _this._readLen); } finally { sem.Release(); } }