示例#1
0
        private void ExecuteAspCompatCode()
        {
            MarkCallContext(this);

            try {
                bool sync = true;

                if (_context != null)
                {
                    ThreadContext threadContext = null;
                    try {
                        threadContext = _app.OnThreadEnter();
                        _error        = _app.ExecuteStep(this, ref sync);
                    }
                    finally {
                        if (threadContext != null)
                        {
                            threadContext.DisassociateFromCurrentThread();
                        }
                    }
                }
                else
                {
                    _error = _app.ExecuteStep(this, ref sync);
                }
            }
            finally {
                MarkCallContext(null);
            }
        }
        private void ResumeTasks(bool waitUntilDone, bool onCallerThread)
        {
#if DBG
            Debug.Trace("Async", "TaskManager.ResumeTasks: onCallerThread=" + onCallerThread +
                        ", _tasksCompleted=" + _tasksCompleted + ", _tasksStarted=" + _tasksStarted);

            if (waitUntilDone)
            {
                // must be on caller thread to wait
                Debug.Assert(onCallerThread);
            }
#endif

            // artifically increment the task count by one
            // to make sure _tasksCompleted doesn't become equal to _tasksStarted during this method
            Interlocked.Increment(ref _tasksStarted);

            try {
                if (onCallerThread)
                {
                    ResumeTasksPossiblyUnderLock(waitUntilDone);
                }
                else
                {
                    using (_app.Context.SyncContext.AcquireThreadLock()) {
                        ThreadContext threadContext = null;
                        try {
                            threadContext = _app.OnThreadEnter();
                            ResumeTasksPossiblyUnderLock(waitUntilDone);
                        }
                        finally {
                            if (threadContext != null)
                            {
                                threadContext.DisassociateFromCurrentThread();
                            }
                        }
                    }
                }
            }
            finally {
                // complete the bogus task introduced with incrementing _tasksStarted at the beginning
                TaskCompleted(onCallerThread);
            }
        }
        private void CompleteTask(bool timedOut, bool syncTimeoutCaller)
        {
            if (Interlocked.Exchange(ref _completionMethodLock, 1) != 0)
            {
                return;
            }

            bool needSetupThreadContext;
            bool responseEnded = false;

            if (timedOut)
            {
                needSetupThreadContext = !syncTimeoutCaller;
            }
            else
            {
                _completedSynchronously = _asyncResult.CompletedSynchronously;
                needSetupThreadContext  = !_completedSynchronously;
            }

            // call the completion or timeout handler
            //  when neeeded setup the thread context and lock
            //  catch and remember all exceptions

            HttpApplication app = _taskManager.Application;

            try {
                if (needSetupThreadContext)
                {
                    using (app.Context.SyncContext.AcquireThreadLock()) {
                        ThreadContext threadContext = null;
                        try {
                            threadContext = app.OnThreadEnter();
                            if (timedOut)
                            {
                                if (_timeoutHandler != null)
                                {
                                    _timeoutHandler(_asyncResult);
                                }
                            }
                            else
                            {
                                _endHandler(_asyncResult);
                            }
                        }
                        finally {
                            if (threadContext != null)
                            {
                                threadContext.DisassociateFromCurrentThread();
                            }
                        }
                    }
                }
                else
                {
                    if (timedOut)
                    {
                        if (_timeoutHandler != null)
                        {
                            _timeoutHandler(_asyncResult);
                        }
                    }
                    else
                    {
                        _endHandler(_asyncResult);
                    }
                }
            }
            catch (ThreadAbortException e) {
                _error = e;

                HttpApplication.CancelModuleException exceptionState = e.ExceptionState as HttpApplication.CancelModuleException;

                // Is this from Response.End()
                if (exceptionState != null && !exceptionState.Timeout)
                {
                    // Mark the request as completed
                    using (app.Context.SyncContext.AcquireThreadLock()) {
                        // Handle response end once. Skip if already initiated (previous AsyncTask)
                        if (!app.IsRequestCompleted)
                        {
                            responseEnded = true;
                            app.CompleteRequest();
                        }
                    }

                    // Clear the error for Response.End
                    _error = null;
                }

                // ---- the exception. Async completion required (DDB 140655)
                Thread.ResetAbort();
            }
            catch (Exception e) {
                _error = e;
            }


            // Complete the current async task
            _completed = true;
            _taskManager.TaskCompleted(_completedSynchronously /*onCallerThread*/); // notify TaskManager

            // Wait for pending AsyncTasks (DDB 140655)
            if (responseEnded)
            {
                _taskManager.CompleteAllTasksNow(false /*syncCaller*/);
            }
        }
        internal static int ProcessRequestNotificationHelper(
            IntPtr rootedObjectsPointer,
            IntPtr nativeRequestContext,
            IntPtr moduleData,
            int flags)
        {
            IIS7WorkerRequest         wr      = null;
            HttpContext               context = null;
            RequestNotificationStatus status  = RequestNotificationStatus.Continue;
            RootedObjects             root;
            bool workerRequestWasJustCreated = false;

            if (rootedObjectsPointer == IntPtr.Zero)
            {
                InitializeRequestContext(nativeRequestContext, flags, out wr, out context);
                workerRequestWasJustCreated = true;
                if (context == null)
                {
                    return((int)RequestNotificationStatus.FinishRequest);
                }

                root               = RootedObjects.Create();
                root.HttpContext   = context;
                root.WorkerRequest = wr;
                root.WriteTransferEventIfNecessary();
                context.RootedObjects = root;

                IIS.MgdSetManagedHttpContext(nativeRequestContext, root.Pointer);
            }
            else
            {
                root    = RootedObjects.FromPointer(rootedObjectsPointer);
                context = root.HttpContext;
                wr      = root.WorkerRequest as IIS7WorkerRequest;
            }

            Debug.Assert(root != null, "We should have a RootedObjects instance by this point.");
            Debug.Assert(wr != null, "We should have an IIS7WorkerRequest instance by this point.");

            using (root.WithinTraceBlock()) {
                if (workerRequestWasJustCreated)
                {
                    AspNetEventSource.Instance.RequestStarted(wr);
                }

                int  currentModuleIndex;
                bool isPostNotification;
                int  currentNotification;
                IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);

                // If the HttpContext is null at this point, then we've already transitioned this request to a WebSockets request.
                // The WebSockets module should already be running, and asynchronous module-level events (like SendResponse) are
                // ineligible to be hooked by managed code.
                if (context == null || context.HasWebSocketRequestTransitionStarted)
                {
                    return((int)RequestNotificationStatus.Continue);
                }

                // It is possible for a notification to complete asynchronously while we're in
                // a call to IndicateCompletion, in which case a new IIS thread might enter before
                // the call to IndicateCompletion returns.  If this happens, block the thread until
                // IndicateCompletion returns.  But never block a SendResponse notification, because
                // that can cause the request to hang (DevDiv Bugs 187441).
                if (context.InIndicateCompletion &&
                    context.ThreadInsideIndicateCompletion != Thread.CurrentThread &&
                    RequestNotification.SendResponse != (RequestNotification)currentNotification)
                {
                    while (context.InIndicateCompletion)
                    {
                        Thread.Sleep(10);
                    }
                }

                // RQ_SEND_RESPONSE fires out of band and completes synchronously only.
                // The pipeline must be reentrant to support this, so the notification
                // context for the previous notification must be saved and restored.
                NotificationContext savedNotificationContext = context.NotificationContext;
                bool cancellable = context.IsInCancellablePeriod;
                bool locked      = false;
                try {
                    if (cancellable)
                    {
                        context.EndCancellablePeriod();
                    }
                    bool isReEntry = (savedNotificationContext != null);
                    if (isReEntry)
                    {
                        context.ApplicationInstance.AcquireNotifcationContextLock(ref locked);
                    }
                    context.NotificationContext = new NotificationContext(flags /*CurrentNotificationFlags*/,
                                                                          isReEntry);
                    status = HttpRuntime.ProcessRequestNotification(wr, context);
                }
                finally {
                    if (status != RequestNotificationStatus.Pending)
                    {
                        // if we completed the notification, pop the notification context stack
                        // if this is an asynchronous unwind, then the completion will clear the context
                        context.NotificationContext = savedNotificationContext;

                        // DevDiv 112755 restore cancellable state if its changed
                        if (cancellable && !context.IsInCancellablePeriod)
                        {
                            context.BeginCancellablePeriod();
                        }
                        else if (!cancellable && context.IsInCancellablePeriod)
                        {
                            context.EndCancellablePeriod();
                        }
                    }
                    if (locked)
                    {
                        context.ApplicationInstance.ReleaseNotifcationContextLock();
                    }
                }

                if (status != RequestNotificationStatus.Pending)
                {
                    // The current notification may have changed due to the HttpApplication progressing the IIS state machine, so retrieve the info again.
                    IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);

                    // WOS 1785741: (Perf) In profiles, 8% of HelloWorld is transitioning from native to managed.
                    // The fix is to keep managed code on the stack so that the AppDomain context remains on the
                    // thread, and we can re-enter managed code without setting up the AppDomain context.
                    // If this optimization is possible, MgdIndicateCompletion will execute one or more notifications
                    // and return PENDING as the status.
                    ThreadContext threadContext = context.IndicateCompletionContext;
                    // DevDiv 482614:
                    // Don't use local copy to detect if we can call MgdIndicateCompletion because another thread
                    // unwinding from MgdIndicateCompletion may be changing context.IndicateCompletionContext at the same time.
                    if (!context.InIndicateCompletion && context.IndicateCompletionContext != null)
                    {
                        if (status == RequestNotificationStatus.Continue)
                        {
                            try {
                                context.InIndicateCompletion = true;
                                Interlocked.Increment(ref _inIndicateCompletionCount);
                                context.ThreadInsideIndicateCompletion = Thread.CurrentThread;
                                IIS.MgdIndicateCompletion(nativeRequestContext, ref status);
                            }
                            finally {
                                context.ThreadInsideIndicateCompletion = null;
                                Interlocked.Decrement(ref _inIndicateCompletionCount);

                                // Leave will have been called already if the last notification is returning pending
                                // DTS267762: Make sure InIndicateCompletion is released, not based on the thread context state
                                // Otherwise the next request notification may deadlock
                                if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion)
                                {
                                    lock (threadContext) {
                                        if (!threadContext.HasBeenDisassociatedFromThread)
                                        {
                                            threadContext.DisassociateFromCurrentThread();
                                        }

                                        context.IndicateCompletionContext = null;
                                        context.InIndicateCompletion      = false;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion)
                            {
                                lock (threadContext) {
                                    if (!threadContext.HasBeenDisassociatedFromThread)
                                    {
                                        threadContext.DisassociateFromCurrentThread();
                                    }

                                    context.IndicateCompletionContext = null;
                                    context.InIndicateCompletion      = false;
                                }
                            }
                        }
                    }
                }

                if (context.HasWebSocketRequestTransitionStarted && status == RequestNotificationStatus.Pending)
                {
                    // At this point, the WebSocket module event (PostEndRequest) has executed and set up the appropriate contexts for us.
                    // However, there is a race condition that we need to avoid. It is possible that one thread has kicked off some async
                    // work, e.g. via an IHttpAsyncHandler, and that thread is unwinding and has reached this line of execution.
                    // Meanwhile, the IHttpAsyncHandler completed quickly (but asynchronously) and invoked MgdPostCompletion, which
                    // resulted in a new thread calling ProcessRequestNotification. If this second thread starts the WebSocket transition,
                    // then there's the risk that *both* threads might attempt to call WebSocketPipeline.ProcessRequest, which could AV
                    // the process.
                    //
                    // We protect against this by allowing only the thread which started the transition to complete the transition, so in
                    // the above scenario the original thread (which invoked the IHttpAsyncHandler) no-ops at this point and just returns
                    // Pending to its caller.

                    if (context.DidCurrentThreadStartWebSocketTransition)
                    {
                        // We'll mark the HttpContext as complete, call the continuation to kick off the socket send / receive loop, and return
                        // Pending to IIS so that it doesn't advance the state machine until the WebSocket loop completes.
                        root.ReleaseHttpContext();
                        root.WebSocketPipeline.ProcessRequest();
                    }
                }

                return((int)status);
            }
        }