private async Task CloseOutputAsyncCore(WebSocketCloseStatus closeStatus,
string statusDescription,
CancellationToken cancellationToken)
{
string inputParameter = string.Empty;
if (NetEventSource.IsEnabled)
{
inputParameter = string.Format(CultureInfo.InvariantCulture,
"closeStatus: {0}, statusDescription: {1}",
closeStatus,
statusDescription);
NetEventSource.Enter(this, inputParameter);
}
try
{
ThrowIfPendingException();
if (IsStateTerminal(State))
{
return;
}
ThrowIfDisposed();
bool thisLockTaken = false;
bool sessionHandleLockTaken = false;
bool needToCompleteSendOperation = false;
bool ownsCloseOutputCancellationTokenSource = false;
bool ownsSendCancellationTokenSource = false;
CancellationToken linkedCancellationToken = CancellationToken.None;
try
{
TakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
ThrowIfPendingException();
ThrowIfDisposed();
if (IsStateTerminal(State))
{
return;
}
ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived);
ownsCloseOutputCancellationTokenSource = _closeOutputOutstandingOperationHelper.TryStartOperation(cancellationToken, out linkedCancellationToken);
if (!ownsCloseOutputCancellationTokenSource)
{
Task closeOutputTask = _closeOutputTask;
if (closeOutputTask != null)
{
ReleaseLocks(ref thisLockTaken, ref sessionHandleLockTaken);
await closeOutputTask.SuppressContextFlow();
TakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
}
}
else
{
needToCompleteSendOperation = true;
while (!(ownsSendCancellationTokenSource =
_sendOutstandingOperationHelper.TryStartOperation(cancellationToken,
out linkedCancellationToken)))
{
if (_keepAliveTask != null)
{
Task keepAliveTask = _keepAliveTask;
ReleaseLocks(ref thisLockTaken, ref sessionHandleLockTaken);
await keepAliveTask.SuppressContextFlow();
TakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
ThrowIfPendingException();
}
else
{
throw new InvalidOperationException(
SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, nameof(SendAsync)));
}
_sendOutstandingOperationHelper.CompleteOperation(ownsSendCancellationTokenSource);
}
EnsureCloseOutputOperation();
_closeOutputOperation.CloseStatus = closeStatus;
_closeOutputOperation.CloseReason = statusDescription;
_closeOutputTask = _closeOutputOperation.Process(null, linkedCancellationToken);
ReleaseLocks(ref thisLockTaken, ref sessionHandleLockTaken);
await _closeOutputTask.SuppressContextFlow();
TakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
if (OnCloseOutputCompleted())
{
bool callCompleteOnCloseCompleted = false;
try
{
callCompleteOnCloseCompleted = await StartOnCloseCompleted(
thisLockTaken, sessionHandleLockTaken, linkedCancellationToken).SuppressContextFlow();
}
catch (Exception)
{
// If an exception is thrown we know that the locks have been released,
// because we enforce IWebSocketStream.CloseNetworkConnectionAsync to yield
ResetFlagsAndTakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
throw;
}
if (callCompleteOnCloseCompleted)
{
ResetFlagsAndTakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
FinishOnCloseCompleted();
}
}
}
}
catch (Exception exception)
{
bool aborted = linkedCancellationToken.IsCancellationRequested;
Abort();
ThrowIfConvertibleException(nameof(CloseOutputAsync), exception, cancellationToken, aborted);
throw;
}
finally
{
_closeOutputOutstandingOperationHelper.CompleteOperation(ownsCloseOutputCancellationTokenSource);
if (needToCompleteSendOperation)
{
_sendOutstandingOperationHelper.CompleteOperation(ownsSendCancellationTokenSource);
}
_closeOutputTask = null;
ReleaseLocks(ref thisLockTaken, ref sessionHandleLockTaken);
}
}
finally
{
if (NetEventSource.IsEnabled)
{
NetEventSource.Exit(this, inputParameter);
}
}
}