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); } }