public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
if (buffer.Length - offset < count)
throw new ArgumentException(SR.Argument_InvalidOffLen);
// Fast path check for cancellation already requested
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled<int>(cancellationToken);
EnsureNotClosed();
EnsureCanWrite();
// Try to satisfy the request from the buffer synchronously. But still need a sem-lock in case that another
// Async IO Task accesses the buffer concurrently. If we fail to acquire the lock without waiting, make this
// an Async operation.
SemaphoreSlim sem = LazyEnsureAsyncActiveSemaphoreInitialized();
Task semaphoreLockTask = sem.WaitAsync();
if (semaphoreLockTask.Status == TaskStatus.RanToCompletion)
{
bool completeSynchronously = true;
try
{
if (_writePos == 0)
ClearReadBufferBeforeWrite();
Debug.Assert(_writePos < _bufferSize);
// If the write completely fits into the buffer, we can complete synchronously:
completeSynchronously = (count < _bufferSize - _writePos);
if (completeSynchronously)
{
Exception error;
WriteToBuffer(buffer, ref offset, ref count, out error);
Debug.Assert(count == 0);
return (error == null)
? Task.CompletedTask
: Task.FromException(error);
}
}
finally
{
if (completeSynchronously) // if this is FALSE, we will be entering WriteToUnderlyingStreamAsync and releasing there.
sem.Release();
}
}
// Delegate to the async implementation.
return WriteToUnderlyingStreamAsync(buffer, offset, count, cancellationToken, semaphoreLockTask);
}