// Calls neighbor.BeginClose or EndClose and catches appropriate exceptions for any cleanup.
        // We use a single method for both BeginClose and EndClose processing since exception handling
        // is very similar in both cases.
        void InvokeAsyncNeighborClose(PeerNeighbor neighbor, PeerCloseReason closeReason,
            PeerCloseInitiator closeInitiator, Exception closeException, IAsyncResult endResult)
        {
            // initiate invoking BeginClose or EndClose
            try
            {
                if (endResult == null)
                {
                    IAsyncResult beginResult = neighbor.BeginClose(closeReason, closeInitiator,
                                        closeException, Fx.ThunkCallback(new AsyncCallback(OnNeighborClosedCallback)), neighbor);
                    if (beginResult.CompletedSynchronously)
                        neighbor.EndClose(beginResult);
                }
                else
                {
                    neighbor.EndClose(endResult);
                }
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e)) throw;
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);

                neighbor.TraceEventHelper(TraceEventType.Warning, TraceCode.PeerNeighborCloseFailed, SR.GetString(SR.TraceCodePeerNeighborCloseFailed), e);
                // May get InvalidOperationException or ObjectDisposedException due to simultaneous close from both sides (and autoclose is enabled)
                if (e is InvalidOperationException || e is CommunicationException || e is TimeoutException)
                {
                    neighbor.Abort();
                }
                else
                {
                    throw;
                }
            }
        }