private void CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state) { ThreadContext threadContext = null; try { threadContext = _application.OnThreadEnter(); try { callback(state); } catch (Exception e) { _error = ExceptionDispatchInfo.Capture(e); } } finally { if (threadContext != null) { threadContext.DisassociateFromCurrentThread(); } } }
ISyncContextLock ISyncContext.Enter() { // Restores impersonation, Culture, etc. ThreadContext threadContext = new ThreadContext(_httpContext); threadContext.AssociateWithCurrentThread(_httpContext.UsesImpersonation); return threadContext; }
// Initializes the thread on entry to the managed pipeline. A ThreadContext is returned, on // which the caller must call Leave. The IIS7 integrated pipeline uses setImpersonationContext // to prevent it from being set until after the authentication notification. // OnThreadEnterPrivate returns ThreadContext. // ThreadContext.Enter sets variables that are stored on the thread, // and saves anything currently on the thread so it can be restored // during the call to ThreadContext.Leave. All variables that are // modified on the thread should be stored in ThreadContext so they // can be restored later. ThreadContext.Enter should only be called // when holding a lock on the HttpApplication instance. // ThreadContext.Leave is also normally called under the lock, but // the Integrated Pipeline may delay this call until after the call to // IndicateCompletion returns. When IndicateCompletion is called, // IIS7 will execute the remaining notifications for the request on // the current thread. As a performance improvement, we do not call // Leave before calling IndicateCompletion, and we do not call Enter/Leave // for the notifications executed while we are in the call to // IndicateCompletion. But when IndicateCompletion returns, we do not // have a lock on the HttpApplication instance and therefore cannot // modify request state, such as the HttpContext or HttpApplication. // The only thing we can do is restore the state of the thread. // There's one problem, the Culture/UICulture may be changed by // user code that directly updates the values on the current thread, so // before leaving the pipeline we call ThreadContext.Synchronize to // synchronize the values that are stored on the HttpContext with what // is on the thread. Because of this, the next notification will end up using // the Culture/UICulture set by user-code, just as it did on IIS6. private ThreadContext OnThreadEnterPrivate(bool setImpersonationContext) { ThreadContext threadContext = new ThreadContext(_context); threadContext.AssociateWithCurrentThread(setImpersonationContext); // An entry is added to the request timeout manager once per request // and removed in ReleaseAppInstance. if (!_timeoutManagerInitialized) { // ensure Timeout is set (see ASURT 148698) // to avoid ---- getting config later (ASURT 127388) _context.EnsureTimeout(); HttpRuntime.RequestTimeoutManager.Add(_context); _timeoutManagerInitialized = true; } return threadContext; }
// Associates this ThreadContext with the current thread. This restores certain // ambient values associated with the current HttpContext, such as the current // user and cultures. It also sets HttpContext.Current. internal void AssociateWithCurrentThread(bool setImpersonationContext) { Debug.Assert(HttpContext != null); // only to be used when context is available Debug.Assert(Current != this, "This ThreadContext is already associated with this thread."); Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread."); Debug.Trace("OnThread", GetTraceMessage("Enter1")); /* * !! IMPORTANT !! * Keep this logic in [....] with DisassociateFromCurrentThread and EnterExecutionContext. */ // attach http context to the call context _originalHttpContext = DisposableHttpContextWrapper.SwitchContext(HttpContext); // set impersonation on the current thread if (setImpersonationContext) { SetImpersonationContext(); } // set synchronization context for the current thread to support the async pattern _originalSynchronizationContext = AsyncOperationManager.SynchronizationContext; AspNetSynchronizationContextBase aspNetSynchronizationContext = HttpContext.SyncContext; AsyncOperationManager.SynchronizationContext = aspNetSynchronizationContext; // set ETW trace ID Guid g = HttpContext.WorkerRequest.RequestTraceIdentifier; if (g != Guid.Empty) { CallContext.LogicalSetData("E2ETrace.ActivityID", g); } // set SqlDependecyCookie HttpContext.ResetSqlDependencyCookie(); // set principal on the current thread _originalThreadPrincipal = Thread.CurrentPrincipal; HttpApplication.SetCurrentPrincipalWithAssert(HttpContext.User); // only set culture on the current thread if it is not initialized SetRequestLevelCulture(); // DevDivBugs 75042 // set current thread in context if there is not there // the timeout manager uses this to abort the correct thread if (HttpContext.CurrentThread == null) { _setCurrentThreadOnHttpContext = true; HttpContext.CurrentThread = Thread.CurrentThread; } // Store a reference to the original ThreadContext.Current. It is possible that a parent // ThreadContext might already be associated with the current thread, e.g. if the current // stack contains a call to MgdIndicateCompletion (via // PipelineRuntime.ProcessRequestNotificationHelper). If this is the case, the child // ThreadContext will temporarily take over. _originalThreadContextCurrent = Current; Current = this; Debug.Trace("OnThread", GetTraceMessage("Enter2")); }
internal override void ResumeSteps(Exception error) { bool completed = false; bool completedSynchronously = true; var application = _application; var context = application.Context; ThreadContext context2 = null; var syncContext = context.SyncContext; lock (_application) { try { context2 = application.OnThreadEnter(); } catch (Exception exception) { if (error == null) { error = exception; } } // try { Label: if (syncContext.Error != null) { error = syncContext.Error; syncContext.ClearError(); } if (error != null) { application.RecordError(error); error = null; } if (syncContext.PendingOperationsCount > 0) { syncContext.SetLastCompletionWorkItem(this._resumeStepsWaitCallback); } else { if ((_currentStepIndex < _endRequestStepIndex) && ((context.Error != null) || _requestCompleted)) { _currentStepIndex = _endRequestStepIndex; } else { _currentStepIndex++; } if (_currentStepIndex >= _execSteps.Length) { completed = true; } else { _numStepCalls++; context.SyncContext.Enable(); error = application.ExecuteStep(_execSteps[_currentStepIndex], ref completedSynchronously); completedSynchronously = true; if (completedSynchronously) { _numSyncStepCalls++; goto Label; } } } } finally { if (context2 != null) { try { context2.Leave(); } catch { } } } } if (completed) { application.AsyncResult.Complete(_numStepCalls == _numSyncStepCalls, null, null); application.ReleaseAppInstance(); } }
private ThreadContext OnThreadEnterPrivate(bool setImpersonationContext) { ThreadContext context = new ThreadContext(this._context); context.Enter(setImpersonationContext); if (!this._timeoutManagerInitialized) { this._context.EnsureTimeout(); HttpRuntime.RequestTimeoutManager.Add(this._context); this._timeoutManagerInitialized = true; } return context; }