private async Task FlushAsyncInternal(CancellationToken cancellationToken)
{
Debug.Assert(_stream != null);
SemaphoreSlim sem = LazyEnsureAsyncActiveSemaphoreInitialized();
await sem.WaitAsync().ConfigureAwait(false);
try
{
if (_writePos > 0)
{
await FlushWriteAsync(cancellationToken).ConfigureAwait(false);
Debug.Assert(_writePos == 0 && _readPos == 0 && _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 because it
// would be a breaking change.) We opt into ignoring the Flush in that situation.
if (!_stream.CanSeek)
return;
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)
await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
Debug.Assert(_writePos == 0 && _readPos == 0 && _readLen == 0);
return;
}
// We had no data in the buffer, but we still need to tell the underlying stream to flush.
if (_stream.CanWrite)
await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
// There was nothing in the buffer:
Debug.Assert(_writePos == 0 && _readPos == _readLen);
}
finally
{
sem.Release();
}
}