public int RecordInvocation(bool captureCallStack)
 {
     _totalInvocationCount++;
     if (_firstInvocationInfo == null)
     {
         _firstInvocationInfo = InvocationInfo.Capture(captureCallStack);
     }
     return(_totalInvocationCount);
 }
        /// <summary>
        /// Returns an Action that determines whether SynchronizationContext.Send or Post was called after the underlying request finished.
        /// The instrumentation can be a performance hit, so this method should not be called if AppVerifier is not enabled.
        /// </summary>
        /// <param name="syncContext">The ISyncContext (HttpApplication, WebSocketPipeline, etc.) on which to perform the check.</param>
        /// <param name="errorHandler">The listener that can handle verification failures.</param>
        /// <returns>A callback which performs the verification.</returns>
        internal static Action GetSyncContextCheckDelegateImpl(ISyncContext syncContext, Action <AppVerifierException> errorHandler)
        {
            Uri    requestUrl = null;
            object originalThreadContextId = null;

            // collect all of the diagnostic information upfront
            HttpContext originalHttpContext = (syncContext != null) ? syncContext.HttpContext : null;

            if (originalHttpContext != null)
            {
                if (!originalHttpContext.HideRequestResponse && originalHttpContext.Request != null)
                {
                    requestUrl = originalHttpContext.Request.Unvalidated.Url;
                }

                // This will be used as a surrogate for the captured HttpContext so that we don't
                // have a long-lived reference to a heavy object graph. See comments on ThreadContextId
                // for more info.
                originalThreadContextId = originalHttpContext.ThreadContextId;
                originalHttpContext     = null;
            }

            // If the condition passed to this method evaluates to false, we will raise an error to whoever is listening.
            AssertDelegate assert = (condition, errorCode) => {
                long mask = 1L << (int)errorCode;
                // assert only if it was not masked out by a bit set
                bool enableAssert = (AppVerifierErrorCodeEnableAssertMask & mask) == mask;

                if (!condition && enableAssert)
                {
                    // capture the stack only if it was not masked out by a bit set
                    bool           captureStack         = (AppVerifierErrorCodeCollectCallStackMask & mask) == mask;
                    InvocationInfo assertInvocationInfo = InvocationInfo.Capture(captureStack);

                    // header
                    StringBuilder errorString = new StringBuilder();
                    errorString.AppendLine(FormatErrorString(SR.AppVerifier_Title));
                    errorString.AppendLine(FormatErrorString(SR.AppVerifier_Subtitle));
                    errorString.AppendLine();

                    // basic info (about the assert)
                    errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_URL, requestUrl));
                    errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_ErrorCode, (int)errorCode));
                    errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_Description, GetLocalizedDescriptionStringForError(errorCode)));
                    errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_ThreadInfo, assertInvocationInfo.ThreadId, assertInvocationInfo.Timestamp.ToLocalTime()));
                    errorString.AppendLine(assertInvocationInfo.StackTrace.ToString());

                    AppVerifierException ex = new AppVerifierException(errorCode, errorString.ToString());
                    errorHandler(ex);
                    throw ex;
                }
            };

            return(() => {
                try {
                    // Make sure that the ISyncContext is still associated with the same HttpContext that
                    // we captured earlier.
                    HttpContext currentHttpContext = (syncContext != null) ? syncContext.HttpContext : null;
                    object currentThreadContextId = (currentHttpContext != null) ? currentHttpContext.ThreadContextId : null;
                    assert(currentThreadContextId != null && ReferenceEquals(originalThreadContextId, currentThreadContextId), AppVerifierErrorCode.SyncContextSendOrPostCalledAfterRequestCompleted);
                }
                catch (AppVerifierException) {
                    // We want to ---- any exceptions thrown by our verification logic, as the failure
                    // has already been recorded by the appropriate listener. Propagate the original
                    // exception upward.
                }
            });
        }
        /// <summary>
        /// Wraps the Begin* part of a Begin / End method pair to allow for signaling when assertions have been violated.
        /// The instrumentation can be a performance hit, so this method should not be called if AppVerifier is not enabled.
        /// </summary>
        /// <param name="httpApplication">The HttpApplication instance for this request, used to get HttpContext and related items.</param>
        /// <param name="beginMethod">The Begin* part of a Begin / End method pair, likely wrapped in a lambda so only the AsyncCallback and object parameters are exposed.</param>
        /// <param name="originalDelegate">The original user-provided delegate, e.g. the thing that 'beginMethod' wraps. Provided so that we can show correct methods when asserting.</param>
        /// <param name="errorHandler">The listener that can handle verification failures.</param>
        /// <returns>The instrumented Begin* method.</returns>
        internal static Func <AsyncCallback, object, IAsyncResult> WrapBeginMethodImpl(HttpApplication httpApplication, Func <AsyncCallback, object, IAsyncResult> beginMethod, Delegate originalDelegate, Action <AppVerifierException> errorHandler, CallStackCollectionBitMasks callStackMask)
        {
            return((callback, state) => {
                // basic diagnostic info goes at the top since it's used during generation of the error message
                AsyncCallbackInvocationHelper asyncCallbackInvocationHelper = new AsyncCallbackInvocationHelper();
                CallStackCollectionBitMasks myBeginMask = callStackMask & CallStackCollectionBitMasks.AllBeginMask;
                bool captureBeginStack = (myBeginMask & (CallStackCollectionBitMasks)AppVerifierCollectCallStackMask) == myBeginMask;

                InvocationInfo beginHandlerInvocationInfo = InvocationInfo.Capture(captureBeginStack);
                Uri requestUrl = null;
                RequestNotification?currentNotification = null;
                bool isPostNotification = false;
                Type httpHandlerType = null;

                // need to collect all this up-front since it might go away during the async operation
                if (httpApplication != null)
                {
                    HttpContext context = httpApplication.Context;
                    if (context != null)
                    {
                        if (!context.HideRequestResponse && context.Request != null)
                        {
                            requestUrl = context.Request.Unvalidated.Url;
                        }

                        if (context.NotificationContext != null)
                        {
                            currentNotification = context.NotificationContext.CurrentNotification;
                            isPostNotification = context.NotificationContext.IsPostNotification;
                        }

                        if (context.Handler != null)
                        {
                            httpHandlerType = context.Handler.GetType();
                        }
                    }
                }

                // If the condition passed to this method evaluates to false, we will raise an error to whoever is listening.
                AssertDelegate assert = (condition, errorCode) => {
                    long mask = 1L << (int)errorCode;
                    // assert only if it was not masked out by a bit set
                    bool enableAssert = (AppVerifierErrorCodeEnableAssertMask & mask) == mask;

                    if (!condition && enableAssert)
                    {
                        // capture the stack only if it was not masked out by a bit set
                        bool captureStack = (AppVerifierErrorCodeCollectCallStackMask & mask) == mask;

                        InvocationInfo assertInvocationInfo = InvocationInfo.Capture(captureStack);

                        // header
                        StringBuilder errorString = new StringBuilder();
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_Title));
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_Subtitle));
                        errorString.AppendLine();

                        // basic info (about the assert)
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_URL, requestUrl));
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_ErrorCode, (int)errorCode));
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_Description, GetLocalizedDescriptionStringForError(errorCode)));
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BasicInfo_ThreadInfo, assertInvocationInfo.ThreadId, assertInvocationInfo.Timestamp.ToLocalTime()));
                        errorString.AppendLine(assertInvocationInfo.StackTrace.ToString());

                        // Begin* method info
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BeginMethodInfo_EntryMethod, PrettyPrintDelegate(originalDelegate)));
                        if (currentNotification != null)
                        {
                            errorString.AppendLine(FormatErrorString(SR.AppVerifier_BeginMethodInfo_RequestNotification_Integrated, currentNotification, isPostNotification));
                        }
                        else
                        {
                            errorString.AppendLine(FormatErrorString(SR.AppVerifier_BeginMethodInfo_RequestNotification_NotIntegrated));
                        }
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BeginMethodInfo_CurrentHandler, httpHandlerType));
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_BeginMethodInfo_ThreadInfo, beginHandlerInvocationInfo.ThreadId, beginHandlerInvocationInfo.Timestamp.ToLocalTime()));
                        errorString.AppendLine(beginHandlerInvocationInfo.StackTrace.ToString());

                        // AsyncCallback info
                        int totalAsyncInvocationCount;
                        InvocationInfo firstAsyncInvocation = asyncCallbackInvocationHelper.GetFirstInvocationInfo(out totalAsyncInvocationCount);
                        errorString.AppendLine(FormatErrorString(SR.AppVerifier_AsyncCallbackInfo_InvocationCount, totalAsyncInvocationCount));
                        if (firstAsyncInvocation != null)
                        {
                            errorString.AppendLine(FormatErrorString(SR.AppVerifier_AsyncCallbackInfo_FirstInvocation_ThreadInfo, firstAsyncInvocation.ThreadId, firstAsyncInvocation.Timestamp.ToLocalTime()));
                            errorString.AppendLine(firstAsyncInvocation.StackTrace.ToString());
                        }

                        AppVerifierException ex = new AppVerifierException(errorCode, errorString.ToString());
                        errorHandler(ex);
                        throw ex;
                    }
                };

                assert(httpApplication != null, AppVerifierErrorCode.HttpApplicationInstanceWasNull);
                assert(originalDelegate != null, AppVerifierErrorCode.BeginHandlerDelegateWasNull);

                object lockObj = new object(); // used to synchronize access to certain locals which can be touched by multiple threads
                IAsyncResult asyncResultReturnedByBeginHandler = null;
                IAsyncResult asyncResultPassedToCallback = null;
                object beginHandlerReturnValueHolder = null;                                  // used to hold the IAsyncResult returned by or Exception thrown by BeginHandler; see comments on Holder<T> for more info
                Thread threadWhichCalledBeginHandler = Thread.CurrentThread;                  // used to determine whether the callback was invoked synchronously
                bool callbackRanToCompletion = false;                                         // don't need to lock when touching this local since it's only read in the synchronous case

                HttpContext assignedContextUponCallingBeginHandler = httpApplication.Context; // used to determine whether the underlying request disappeared

                try {
                    asyncResultReturnedByBeginHandler = beginMethod(
                        asyncResult => {
                        try {
                            CallStackCollectionBitMasks myCallbackMask = callStackMask & CallStackCollectionBitMasks.AllCallbackMask;
                            bool captureEndCallStack = (myCallbackMask & AppVerifierCollectCallStackMask) == myCallbackMask;
                            // The callback must never be called more than once.
                            int newAsyncCallbackInvocationCount = asyncCallbackInvocationHelper.RecordInvocation(captureEndCallStack);
                            assert(newAsyncCallbackInvocationCount == 1, AppVerifierErrorCode.AsyncCallbackInvokedMultipleTimes);

                            // The 'asyncResult' parameter must never be null.
                            assert(asyncResult != null, AppVerifierErrorCode.AsyncCallbackInvokedWithNullParameter);

                            object tempBeginHandlerReturnValueHolder;
                            Thread tempThreadWhichCalledBeginHandler;
                            lock (lockObj) {
                                asyncResultPassedToCallback = asyncResult;
                                tempBeginHandlerReturnValueHolder = beginHandlerReturnValueHolder;
                                tempThreadWhichCalledBeginHandler = threadWhichCalledBeginHandler;
                            }

                            // At this point, 'IsCompleted = true' is mandatory.
                            assert(asyncResult.IsCompleted, AppVerifierErrorCode.AsyncCallbackGivenAsyncResultWhichWasNotCompleted);

                            if (tempBeginHandlerReturnValueHolder == null)
                            {
                                // BeginHandler hasn't yet returned, so this call may be synchronous or asynchronous.
                                // We can tell by comparing the current thread with the thread which called BeginHandler.
                                // From a correctness perspective, it is valid to invoke the AsyncCallback delegate either
                                // synchronously or asynchronously. From [....]: if 'CompletedSynchronously = true', then
                                // AsyncCallback invocation can happen either on the same thread or on a different thread,
                                // just as long as BeginHandler hasn't yet returned (which in true in this case).
                                if (!asyncResult.CompletedSynchronously)
                                {
                                    // If 'CompletedSynchronously = false', we must be on a different thread than the BeginHandler invocation.
                                    assert(tempThreadWhichCalledBeginHandler != Thread.CurrentThread, AppVerifierErrorCode.AsyncCallbackInvokedSynchronouslyButAsyncResultWasNotMarkedCompletedSynchronously);
                                }
                            }
                            else
                            {
                                // BeginHandler already returned, so this invocation is definitely asynchronous.

                                Holder <IAsyncResult> asyncResultHolder = tempBeginHandlerReturnValueHolder as Holder <IAsyncResult>;
                                if (asyncResultHolder != null)
                                {
                                    // We need to verify that the IAsyncResult we're given is the same that was returned by BeginHandler
                                    // and that the IAsyncResult is marked 'CompletedSynchronously = false'.
                                    assert(asyncResult == asyncResultHolder.Value, AppVerifierErrorCode.AsyncCallbackInvokedWithUnexpectedAsyncResultInstance);
                                    assert(!asyncResult.CompletedSynchronously, AppVerifierErrorCode.AsyncCallbackInvokedAsynchronouslyButAsyncResultWasMarkedCompletedSynchronously);
                                }
                                else
                                {
                                    // If we reached this point, BeginHandler threw an exception.
                                    // The AsyncCallback should never be invoked if BeginHandler has already failed.
                                    assert(false, AppVerifierErrorCode.BeginHandlerThrewThenAsyncCallbackInvokedAsynchronously);
                                }
                            }

                            // AsyncState must match the 'state' parameter passed to BeginHandler
                            assert(asyncResult.AsyncState == state, AppVerifierErrorCode.AsyncCallbackInvokedWithUnexpectedAsyncResultAsyncState);

                            // Make sure the underlying HttpApplication is still assigned to the captured HttpContext instance.
                            // If not, this AsyncCallback invocation could end up completing *some other request's* operation,
                            // resulting in data corruption.
                            assert(assignedContextUponCallingBeginHandler == httpApplication.Context, AppVerifierErrorCode.AsyncCallbackCalledAfterHttpApplicationReassigned);
                        }
                        catch (AppVerifierException) {
                            // We want to ---- any exceptions thrown by our verification logic, as the failure
                            // has already been recorded by the appropriate listener. Just go straight to
                            // invoking the callback.
                        }

                        // all checks complete - delegate control to the actual callback
                        if (callback != null)
                        {
                            callback(asyncResult);
                        }
                        callbackRanToCompletion = true;
                    },
                        state);

                    // The return value must never be null.
                    assert(asyncResultReturnedByBeginHandler != null, AppVerifierErrorCode.BeginHandlerReturnedNull);

                    lock (lockObj) {
                        beginHandlerReturnValueHolder = new Holder <IAsyncResult>(asyncResultReturnedByBeginHandler);
                    }

                    if (asyncResultReturnedByBeginHandler.CompletedSynchronously)
                    {
                        // If 'CompletedSynchronously = true', the IAsyncResult must be marked 'IsCompleted = true'
                        // and the AsyncCallback must have been invoked synchronously (checked in the AsyncCallback verification logic).
                        assert(asyncResultReturnedByBeginHandler.IsCompleted, AppVerifierErrorCode.BeginHandlerReturnedAsyncResultMarkedCompletedSynchronouslyButWhichWasNotCompleted);
                        assert(asyncCallbackInvocationHelper.TotalInvocations != 0, AppVerifierErrorCode.BeginHandlerReturnedAsyncResultMarkedCompletedSynchronouslyButAsyncCallbackNeverCalled);
                    }

                    IAsyncResult tempAsyncResultPassedToCallback;
                    lock (lockObj) {
                        tempAsyncResultPassedToCallback = asyncResultPassedToCallback;
                    }

                    // The AsyncCallback may have been invoked (either synchronously or asynchronously). If it has been
                    // invoked, we need to verify that it was given the same IAsyncResult returned by BeginHandler.
                    // If the AsyncCallback hasn't yet been called, we skip this check, as the AsyncCallback verification
                    // logic will eventually perform the check at the appropriate time.
                    if (tempAsyncResultPassedToCallback != null)
                    {
                        assert(tempAsyncResultPassedToCallback == asyncResultReturnedByBeginHandler, AppVerifierErrorCode.BeginHandlerReturnedUnexpectedAsyncResultInstance);
                    }

                    // AsyncState must match the 'state' parameter passed to BeginHandler
                    assert(asyncResultReturnedByBeginHandler.AsyncState == state, AppVerifierErrorCode.BeginHandlerReturnedUnexpectedAsyncResultAsyncState);

                    // all checks complete
                    return asyncResultReturnedByBeginHandler;
                }
                catch (AppVerifierException) {
                    // We want to ---- any exceptions thrown by our verification logic, as the failure
                    // has already been recorded by the appropriate listener. Just return the original
                    // IAsyncResult so that the application continues to run.
                    return asyncResultReturnedByBeginHandler;
                }
                catch (Exception ex) {
                    if (asyncResultReturnedByBeginHandler == null)
                    {
                        // If we reached this point, an exception was thrown by BeginHandler, so we need to
                        // record it and rethrow it.

                        IAsyncResult tempAsyncResultPassedToCallback;
                        lock (lockObj) {
                            beginHandlerReturnValueHolder = new Holder <Exception>(ex);
                            tempAsyncResultPassedToCallback = asyncResultPassedToCallback;
                        }

                        try {
                            // The AsyncCallback should only be invoked if BeginHandler ran to completion.
                            if (tempAsyncResultPassedToCallback != null)
                            {
                                // If AsyncCallback was invoked asynchronously, then by definition it was
                                // scheduled prematurely since BeginHandler hadn't yet run to completion
                                // (since whatever additional work it did after invoking the callback failed).
                                // Therefore it is always wrong for BeginHandler to both throw and
                                // asynchronously invoke AsyncCallback.
                                assert(tempAsyncResultPassedToCallback.CompletedSynchronously, AppVerifierErrorCode.AsyncCallbackInvokedAsynchronouslyThenBeginHandlerThrew);

                                // If AsyncCallback was invoked synchronously, then it must have been invoked
                                // before BeginHandler surfaced the exception (since otherwise BeginHandler
                                // wouldn't have reached the line of code that invoked AsyncCallback). But
                                // AsyncCallback itself could have thrown, bubbling the exception up through
                                // BeginHandler and back to us. If AsyncCallback ran to completion, then this
                                // means BeginHandler did extra work (which failed) after invoking AsyncCallback,
                                // so BeginHandler by definition hadn't yet run to completion.
                                assert(!callbackRanToCompletion, AppVerifierErrorCode.AsyncCallbackInvokedSynchronouslyThenBeginHandlerThrew);
                            }
                        }
                        catch (AppVerifierException) {
                            // We want to ---- any exceptions thrown by our verification logic, as the failure
                            // has already been recorded by the appropriate listener. Propagate the original
                            // exception upward.
                        }

                        throw;
                    }
                    else
                    {
                        // We want to ---- any exceptions thrown by our verification logic, as the failure
                        // has already been recorded by the appropriate listener. Just return the original
                        // IAsyncResult so that the application continues to run.
                        return asyncResultReturnedByBeginHandler;
                    }
                }
                finally {
                    // Since our local variables are GC-rooted in an anonymous object, we should
                    // clear references to objects we no longer need so that the GC can reclaim
                    // them if appropriate.
                    lock (lockObj) {
                        threadWhichCalledBeginHandler = null;
                    }
                }
            });
        }