System.Net.WebSockets.WebSocketBase.CloseAsyncCore C# (CSharp) Method

CloseAsyncCore() private method

private CloseAsyncCore ( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken ) : Task
closeStatus WebSocketCloseStatus
statusDescription string
cancellationToken System.Threading.CancellationToken
return Task
        private async Task CloseAsyncCore(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 lockTaken = false;
                Monitor.Enter(_thisLock, ref lockTaken);
                bool ownsCloseCancellationTokenSource = false;
                CancellationToken linkedCancellationToken = CancellationToken.None;
                try
                {
                    ThrowIfPendingException();
                    if (IsStateTerminal(State))
                    {
                        return;
                    }
                    ThrowIfDisposed();
                    ThrowOnInvalidState(State,
                        WebSocketState.Open, WebSocketState.CloseReceived, WebSocketState.CloseSent);

                    Task closeOutputTask;
                    ownsCloseCancellationTokenSource = _closeOutstandingOperationHelper.TryStartOperation(cancellationToken, out linkedCancellationToken);
                    if (ownsCloseCancellationTokenSource)
                    {
                        closeOutputTask = _closeOutputTask;
                        if (closeOutputTask == null && State != WebSocketState.CloseSent)
                        {
                            if (_closeReceivedTaskCompletionSource == null)
                            {
                                _closeReceivedTaskCompletionSource = new TaskCompletionSource<object>();
                            }

                            closeOutputTask = CloseOutputAsync(closeStatus,
                                statusDescription,
                                linkedCancellationToken);
                        }
                    }
                    else
                    {
                        Debug.Assert(_closeReceivedTaskCompletionSource != null,
                            "'_closeReceivedTaskCompletionSource' MUST NOT be NULL.");
                        closeOutputTask = _closeReceivedTaskCompletionSource.Task;
                    }

                    if (closeOutputTask != null)
                    {
                        ReleaseLock(_thisLock, ref lockTaken);
                        try
                        {
                            await closeOutputTask.SuppressContextFlow();
                        }
                        catch (Exception closeOutputError)
                        {
                            Monitor.Enter(_thisLock, ref lockTaken);

                            if (!CanHandleExceptionDuringClose(closeOutputError))
                            {
                                ThrowIfConvertibleException(nameof(CloseOutputAsync),
                                    closeOutputError,
                                    cancellationToken,
                                    linkedCancellationToken.IsCancellationRequested);
                                throw;
                            }
                        }

                        // When closeOutputTask != null  and an exception thrown from await closeOutputTask is handled, 
                        // the lock will be taken in the catch-block. So the logic here avoids taking the lock twice. 
                        if (!lockTaken)
                        {
                            Monitor.Enter(_thisLock, ref lockTaken);
                        }
                    }

                    if (OnCloseOutputCompleted())
                    {
                        bool callCompleteOnCloseCompleted = false;

                        try
                        {
                            // linkedCancellationToken can be CancellationToken.None if ownsCloseCancellationTokenSource==false
                            // This is still ok because OnCloseOutputCompleted won't start any IO operation in this case
                            callCompleteOnCloseCompleted = await StartOnCloseCompleted(
                                lockTaken, false, linkedCancellationToken).SuppressContextFlow();
                        }
                        catch (Exception)
                        {
                            // If an exception is thrown we know that the locks have been released,
                            // because we enforce IWebSocketStream.CloseNetworkConnectionAsync to yield
                            ResetFlagAndTakeLock(_thisLock, ref lockTaken);
                            throw;
                        }

                        if (callCompleteOnCloseCompleted)
                        {
                            ResetFlagAndTakeLock(_thisLock, ref lockTaken);
                            FinishOnCloseCompleted();
                        }
                    }

                    if (IsStateTerminal(State))
                    {
                        return;
                    }

                    linkedCancellationToken = CancellationToken.None;

                    bool ownsReceiveCancellationTokenSource = _receiveOutstandingOperationHelper.TryStartOperation(cancellationToken, out linkedCancellationToken);
                    if (ownsReceiveCancellationTokenSource)
                    {
                        _closeAsyncStartedReceive = true;
                        ArraySegment<byte> closeMessageBuffer =
                            new ArraySegment<byte>(new byte[WebSocketBuffer.MinReceiveBufferSize]);
                        EnsureReceiveOperation();
                        Task<WebSocketReceiveResult> receiveAsyncTask = _receiveOperation.Process(closeMessageBuffer,
                            linkedCancellationToken);
                        ReleaseLock(_thisLock, ref lockTaken);

                        WebSocketReceiveResult receiveResult = null;
                        try
                        {
                            receiveResult = await receiveAsyncTask.SuppressContextFlow();
                        }
                        catch (Exception receiveException)
                        {
                            Monitor.Enter(_thisLock, ref lockTaken);

                            if (!CanHandleExceptionDuringClose(receiveException))
                            {
                                ThrowIfConvertibleException(nameof(CloseAsync),
                                    receiveException,
                                    cancellationToken,
                                    linkedCancellationToken.IsCancellationRequested);
                                throw;
                            }
                        }

                        // receiveResult is NEVER NULL if WebSocketBase.ReceiveOperation.Process completes successfully 
                        // - but in the close code path we handle some exception if another thread was able to tranistion 
                        // the state into Closed successfully. In this case receiveResult can be NULL and it is safe to 
                        // skip the statements in the if-block.
                        if (receiveResult != null)
                        {
                            if (NetEventSource.IsEnabled && receiveResult.Count > 0)
                            {
                                NetEventSource.DumpBuffer(this, closeMessageBuffer.Array, closeMessageBuffer.Offset, receiveResult.Count);
                            }

                            if (receiveResult.MessageType != WebSocketMessageType.Close)
                            {
                                throw new WebSocketException(WebSocketError.InvalidMessageType,
                                    SR.Format(SR.net_WebSockets_InvalidMessageType,
                                        typeof(WebSocket).Name + "." + nameof(CloseAsync),
                                        typeof(WebSocket).Name + "." + nameof(CloseOutputAsync),
                                        receiveResult.MessageType));
                            }
                        }
                    }
                    else
                    {
                        _receiveOutstandingOperationHelper.CompleteOperation(ownsReceiveCancellationTokenSource);
                        ReleaseLock(_thisLock, ref lockTaken);
                        await _closeReceivedTaskCompletionSource.Task.SuppressContextFlow();
                    }

                    // When ownsReceiveCancellationTokenSource is true and an exception is thrown, the lock will be taken.
                    // So this logic here is to avoid taking the lock twice. 
                    if (!lockTaken)
                    {
                        Monitor.Enter(_thisLock, ref lockTaken);
                    }

                    if (!IsStateTerminal(State))
                    {
                        bool ownsSendCancellationSource = false;
                        try
                        {
                            // We know that the CloseFrame has been sent at this point. So no Send-operation is allowed anymore and we
                            // can hijack the _SendOutstandingOperationHelper to create a linkedCancellationToken
                            ownsSendCancellationSource = _sendOutstandingOperationHelper.TryStartOperation(cancellationToken, out linkedCancellationToken);
                            Debug.Assert(ownsSendCancellationSource, "'ownsSendCancellationSource' MUST be 'true' at this point.");

                            bool callCompleteOnCloseCompleted = false;

                            try
                            {
                                // linkedCancellationToken can be CancellationToken.None if ownsCloseCancellationTokenSource==false
                                // This is still ok because OnCloseOutputCompleted won't start any IO operation in this case
                                callCompleteOnCloseCompleted = await StartOnCloseCompleted(
                                    lockTaken, false, linkedCancellationToken).SuppressContextFlow();
                            }
                            catch (Exception)
                            {
                                // If an exception is thrown we know that the locks have been released,
                                // because we enforce IWebSocketStream.CloseNetworkConnectionAsync to yield
                                ResetFlagAndTakeLock(_thisLock, ref lockTaken);
                                throw;
                            }

                            if (callCompleteOnCloseCompleted)
                            {
                                ResetFlagAndTakeLock(_thisLock, ref lockTaken);
                                FinishOnCloseCompleted();
                            }
                        }
                        finally
                        {
                            _sendOutstandingOperationHelper.CompleteOperation(ownsSendCancellationSource);
                        }
                    }
                }
                catch (Exception exception)
                {
                    bool aborted = linkedCancellationToken.IsCancellationRequested;
                    Abort();
                    ThrowIfConvertibleException(nameof(CloseAsync), exception, cancellationToken, aborted);
                    throw;
                }
                finally
                {
                    _closeOutstandingOperationHelper.CompleteOperation(ownsCloseCancellationTokenSource);
                    ReleaseLock(_thisLock, ref lockTaken);
                }
            }
            finally
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, inputParameter);
                }
            }
        }