//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ /* * The ASP.NET/IIS 7 integrated pipeline runtime service host * * Copyright (c) 2004 Microsoft Corporation */ namespace System.Web.Hosting { using System.Collections; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Security.Principal; using System.Text; using System.Threading; using System.Web.Util; using System.Web; using System.Web.Management; using System.IO; using IIS = UnsafeIISMethods; delegate void AsyncCompletionDelegate( IntPtr rootedObjectsPointer, int bytesRead, int hresult, IntPtr pAsyncCompletionContext); delegate void AsyncDisconnectNotificationDelegate( IntPtr pManagedRootedObjects); // this delegate is called from native code // each time a native-managed // transition is made to process a request state delegate int ExecuteFunctionDelegate( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags); delegate IntPtr PrincipalFunctionDelegate( IntPtr rootedObjectsPointer, int requestingAppDomainId); delegate int RoleFunctionDelegate( IntPtr pRootedObjects, IntPtr pszRole, int cchRole, out bool isInRole); // this delegate is called from native code when the request is complete // to free any managed resources associated with the request delegate void DisposeFunctionDelegate( [In] IntPtr rootedObjectsPointer ); [ComImport, Guid("c96cb854-aec2-4208-9ada-a86a96860cb6"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)] internal interface IPipelineRuntime { void StartProcessing(); void StopProcessing(); void InitializeApplication([In] IntPtr appContext); IntPtr GetAsyncCompletionDelegate(); IntPtr GetAsyncDisconnectNotificationDelegate(); IntPtr GetExecuteDelegate(); IntPtr GetDisposeDelegate(); IntPtr GetRoleDelegate(); IntPtr GetPrincipalDelegate(); } /// /// /// [To be supplied.] /// /// internal sealed class PipelineRuntime : MarshalByRefObject, IPipelineRuntime, IRegisteredObject { // initialization error handling internal const string InitExceptionModuleName = "AspNetInitializationExceptionModule"; private const string s_InitExceptionModulePrecondition = ""; // to control removal from unmanaged table (to it only once) private static int s_isThisAppDomainRemovedFromUnmanagedTable; private static IntPtr s_ApplicationContext; private static string s_thisAppDomainsIsapiAppId; // when GL_APPLICATION_STOP fires, this is set to true to indicate that we can unload the AppDomain private static bool s_StopProcessingCalled; private static bool s_InitializationCompleted; // keep rooted through the app domain lifetime private static object _delegatelock = new object(); private static int _inIndicateCompletionCount; private static IntPtr _asyncCompletionDelegatePointer = IntPtr.Zero; private static AsyncCompletionDelegate _asyncCompletionDelegate = null; private static IntPtr _asyncDisconnectNotificationDelegatePointer = IntPtr.Zero; private static AsyncDisconnectNotificationDelegate _asyncDisconnectNotificationDelegate = null; private static IntPtr _executeDelegatePointer = IntPtr.Zero; private static ExecuteFunctionDelegate _executeDelegate = null; private static IntPtr _disposeDelegatePointer = IntPtr.Zero; private static DisposeFunctionDelegate _disposeDelegate = null; private static IntPtr _roleDelegatePointer = IntPtr.Zero; private static RoleFunctionDelegate _roleDelegate = null; private static IntPtr _principalDelegatePointer = IntPtr.Zero; private static PrincipalFunctionDelegate _principalDelegate = null; public IntPtr GetAsyncCompletionDelegate() { if (IntPtr.Zero == _asyncCompletionDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _asyncCompletionDelegatePointer) { AsyncCompletionDelegate d = new AsyncCompletionDelegate(AsyncCompletionHandler); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { _asyncCompletionDelegate = d; _asyncCompletionDelegatePointer = p; } } } } } return _asyncCompletionDelegatePointer; } public IntPtr GetAsyncDisconnectNotificationDelegate() { if (IntPtr.Zero == _asyncDisconnectNotificationDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _asyncDisconnectNotificationDelegatePointer) { AsyncDisconnectNotificationDelegate d = new AsyncDisconnectNotificationDelegate(AsyncDisconnectNotificationHandler); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { _asyncDisconnectNotificationDelegate = d; _asyncDisconnectNotificationDelegatePointer = p; } } } } } return _asyncDisconnectNotificationDelegatePointer; } public IntPtr GetExecuteDelegate() { if (IntPtr.Zero == _executeDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _executeDelegatePointer) { ExecuteFunctionDelegate d = new ExecuteFunctionDelegate(ProcessRequestNotification); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { Thread.MemoryBarrier(); _executeDelegate = d; _executeDelegatePointer = p; } } } } } return _executeDelegatePointer; } public IntPtr GetDisposeDelegate() { if (IntPtr.Zero == _disposeDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _disposeDelegatePointer) { DisposeFunctionDelegate d = new DisposeFunctionDelegate(DisposeHandler); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { Thread.MemoryBarrier(); _disposeDelegate = d; _disposeDelegatePointer = p; } } } } } return _disposeDelegatePointer; } public IntPtr GetRoleDelegate() { if (IntPtr.Zero == _roleDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _roleDelegatePointer) { RoleFunctionDelegate d = new RoleFunctionDelegate(RoleHandler); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { Thread.MemoryBarrier(); _roleDelegate = d; _roleDelegatePointer = p; } } } } } return _roleDelegatePointer; } public IntPtr GetPrincipalDelegate() { if (IntPtr.Zero == _principalDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _principalDelegatePointer) { PrincipalFunctionDelegate d = new PrincipalFunctionDelegate(GetManagedPrincipalHandler); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { Thread.MemoryBarrier(); _principalDelegate = d; _principalDelegatePointer = p; } } } } } return _principalDelegatePointer; } [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)] public PipelineRuntime() { HostingEnvironment.RegisterObject(this); Debug.Trace("PipelineDomain", "RegisterObject(this) called"); } [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)] public override Object InitializeLifetimeService() { return null; // never expire lease } public void StartProcessing() { Debug.Trace("PipelineDomain", "StartProcessing AppId = " + s_thisAppDomainsIsapiAppId); HostingEnvironment.SetupStopListeningHandler(); } [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)] public void StopProcessing() { Debug.Trace("PipelineDomain", "StopProcessing with stack = " + Environment.StackTrace + " for AppId= " + s_thisAppDomainsIsapiAppId); if (!HostingEnvironment.StopListeningWasCalled && !HostingEnvironment.ShutdownInitiated) { // If GL_STOP_LISTENING wasn't triggered, the reset is likely due to a configuration change. HttpRuntime.SetShutdownReason(ApplicationShutdownReason.ConfigurationChange, "IIS configuration change"); } s_StopProcessingCalled = true; // inititate shutdown and // require the native callback for Stop HostingEnvironment.InitiateShutdownWithoutDemand(); } internal static void WaitForRequestsToDrain() { if (s_ApplicationContext == IntPtr.Zero) { // If InitializeApplication was never called, then no requests ever came in and StopProcessing will never be called. // We can just short-circuit this method. return; } while (!s_StopProcessingCalled || _inIndicateCompletionCount > 0) { Thread.Sleep(250); } } private StringBuilder FormatExceptionMessage(Exception e, string[] strings) { StringBuilder sb = new StringBuilder(4096); if (null != strings) { for (int i = 0; i < strings.Length; i++) { sb.Append(strings[i]); } } for (Exception current = e; current != null; current = current.InnerException) { if (current == e) sb.Append("\r\n\r\nException: "); else sb.Append("\r\n\r\nInnerException: "); sb.Append(current.GetType().FullName); sb.Append("\r\nMessage: "); sb.Append(current.Message); sb.Append("\r\nStackTrace: "); sb.Append(current.StackTrace); } return sb; } public void InitializeApplication(IntPtr appContext) { s_ApplicationContext = appContext; // DevDiv #381425 - webengine4!RegisterModule runs *after* HostingEnvironment.Initialize (and thus the // HttpRuntime static ctor) when application preload is active. This means that any global state set // by RegisterModule (like the IIS version information, whether we're in integrated mode, misc server // info, etc.) will be unavailable to PreAppStart / preload code when the preload feature is active. // But since RegisterModule runs before InitializeApplication, we have one last chance here to collect // the information before the main part of the application starts, and the pipeline can depend on it // to be accurate. HttpRuntime.PopulateIISVersionInformation(); HttpApplication app = null; try { // if HttpRuntime.HostingInit failed, do not attempt to create the application (WOS #1653963) if (!HttpRuntime.HostingInitFailed) { // // On IIS7, application initialization does not provide an http context. Theoretically, // no one should be using the context during application initialization, but people do. // Create a dummy context that is used during application initialization // to prevent breakage (ISAPI mode always provides a context) // HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("" /*page*/, "" /*query*/, new StringWriter(CultureInfo.InvariantCulture)); MimeMapping.SetIntegratedApplicationContext(appContext); HttpContext initHttpContext = new HttpContext(initWorkerRequest); app = HttpApplicationFactory.GetPipelineApplicationInstance(appContext, initHttpContext); } } catch(Exception e) { if (HttpRuntime.InitializationException == null) { HttpRuntime.InitializationException = e; } } finally { s_InitializationCompleted = true; if (HttpRuntime.InitializationException != null) { // at least one module must be registered so that we // call ProcessRequestNotification later and send the formatted // InitializationException to the client. int hresult = UnsafeIISMethods.MgdRegisterEventSubscription( appContext, InitExceptionModuleName, RequestNotification.BeginRequest, 0 /*postRequestNotifications*/, InitExceptionModuleName, s_InitExceptionModulePrecondition, new IntPtr(-1), false /*useHighPriority*/); if (hresult < 0) { throw new COMException( SR.GetString(SR.Failed_Pipeline_Subscription, InitExceptionModuleName), hresult ); } // Always register a managed handler: // WOS 1990290: VS F5 Debugging: "AspNetInitializationExceptionModule" is registered for RQ_BEGIN_REQUEST, // but the DEBUG verb skips notifications until post RQ_AUTHENTICATE_REQUEST. hresult = UnsafeIISMethods.MgdRegisterEventSubscription( appContext, HttpApplication.IMPLICIT_HANDLER, RequestNotification.ExecuteRequestHandler /*requestNotifications*/, 0 /*postRequestNotifications*/, String.Empty /*type*/, HttpApplication.MANAGED_PRECONDITION /*precondition*/, new IntPtr(-1), false /*useHighPriority*/); if (hresult < 0) { throw new COMException( SR.GetString(SR.Failed_Pipeline_Subscription, HttpApplication.IMPLICIT_HANDLER), hresult ); } } if (app != null) { HttpApplicationFactory.RecyclePipelineApplicationInstance(app); } } } private static HttpContext UnwrapContext(IntPtr rootedObjectsPointer) { RootedObjects objects = RootedObjects.FromPointer(rootedObjectsPointer); return objects.HttpContext; } internal bool HostingShutdownInitiated { get { return HostingEnvironment.ShutdownInitiated; } } // called from native code when the IHttpContext is disposed internal static void AsyncCompletionHandler(IntPtr rootedObjectsPointer, int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext) { HttpContext context = UnwrapContext(rootedObjectsPointer); IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest; wr.OnAsyncCompletion(bytesCompleted, hresult, pAsyncCompletionContext); } // called from native code when the IHttpConnection is disconnected internal static void AsyncDisconnectNotificationHandler(IntPtr pManagedRootedObjects) { // Every object we're about to call into should be live / non-disposed, // but since we're paranoid we should put guard clauses everywhere. Debug.Assert(pManagedRootedObjects != IntPtr.Zero); if (pManagedRootedObjects != IntPtr.Zero) { RootedObjects rootObj = RootedObjects.FromPointer(pManagedRootedObjects); Debug.Assert(rootObj != null); if (rootObj != null) { IIS7WorkerRequest workerRequest = rootObj.WorkerRequest; Debug.Assert(workerRequest != null); if (workerRequest != null) { workerRequest.NotifyOfAsyncDisconnect(); } } } } // Called from native code to see if a principal is in a given role internal static int RoleHandler(IntPtr pRootedObjects, IntPtr pszRole, int cchRole, out bool isInRole) { isInRole = false; IPrincipal principal = RootedObjects.FromPointer(pRootedObjects).Principal; if (principal != null) { try { isInRole = principal.IsInRole(StringUtil.StringFromWCharPtr(pszRole, cchRole)); } catch (Exception e) { return Marshal.GetHRForException(e); } } return HResults.S_OK; } // Called from native code to get the managed principal for a given request // If the return value is non-zero, the caller must free the returned GCHandle internal static IntPtr GetManagedPrincipalHandler(IntPtr pRootedObjects, int requestingAppDomainId) { // DevDiv 375079: Server.TransferRequest can be used to transfer requests to different applications, // which means that we might be trying to pass a GCHandle to the IPrincipal object to a different // AppDomain, which is disallowed. If this happens, we just tell our caller that we can't give him // a managed IPrincipal object. if (requestingAppDomainId != AppDomain.CurrentDomain.Id) { return IntPtr.Zero; } IPrincipal principal = RootedObjects.FromPointer(pRootedObjects).Principal; return GCUtil.RootObject(principal); } // called from native code when the IHttpContext is disposed internal static void DisposeHandler(IntPtr rootedObjectsPointer) { RootedObjects root = RootedObjects.FromPointer(rootedObjectsPointer); root.Destroy(); } // called from managed code as a perf optimization to avoid calling back later internal static void DisposeHandler(HttpContext context, IntPtr nativeRequestContext, RequestNotificationStatus status) { if (IIS.MgdCanDisposeManagedContext(nativeRequestContext, status)) { context.RootedObjects.Destroy(); } } // // This is the managed entry point for processing request notifications. // Although this method is wrapped in try/catch, it is not supposed to // cause an exception. If it does throw, the application, httpwriter, etc // may not be initialized, and it might not be possible to process the rest // of the request. I would prefer to let this method throw and crash the // process, but for now we will consume the exception, report the error to // IIS, and continue. // // Code that might throw belongs in HttpRuntime::ProcessRequestNotificationPrivate. // internal static int ProcessRequestNotification( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags) { try { return ProcessRequestNotificationHelper(rootedObjectsPointer, nativeRequestContext, moduleData, flags); } catch(Exception e) { ApplicationManager.RecordFatalException(e); throw; } } 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); Action verifierCheck = null; if (AppVerifier.IsAppVerifierEnabled) { verifierCheck = AppVerifier.GetRequestNotificationStatusCheckDelegate(context, (RequestNotification)currentNotification, isPostNotification); } status = HttpRuntime.ProcessRequestNotification(wr, context); if (verifierCheck != null) { AppVerifier.InvokeVerifierCheck(verifierCheck, status); } } 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; } } private static void InitializeRequestContext(IntPtr nativeRequestContext, int flags, out IIS7WorkerRequest wr, out HttpContext context) { wr = null; context = null; try { bool etwEnabled = ((flags & HttpContext.FLAG_ETW_PROVIDER_ENABLED) == HttpContext.FLAG_ETW_PROVIDER_ENABLED); // this may throw, e.g. if the request Content-Length header has a value greater than Int32.MaxValue wr = IIS7WorkerRequest.CreateWorkerRequest(nativeRequestContext, etwEnabled); // this may throw, e.g. see WOS 1724573: ASP.Net v2.0: wrong error code returned when ? is used in the URL context = new HttpContext(wr, false); } catch { // treat as "400 Bad Request" since that's the only reason the HttpContext.ctor should throw IIS.MgdSetBadRequestStatus(nativeRequestContext); } } /// /// void IRegisteredObject.Stop(bool immediate) { Debug.Trace("PipelineDomain", "IRegisteredObject.Stop appId = " + s_thisAppDomainsIsapiAppId); while (!s_InitializationCompleted && !s_StopProcessingCalled) { // the native W3_MGD_APP_CONTEXT is not ready for us to unload Thread.Sleep(250); } RemoveThisAppDomainFromUnmanagedTable(); HostingEnvironment.UnregisterObject(this); } internal void SetThisAppDomainsIsapiAppId(String appId) { Debug.Trace("PipelineDomain", "SetThisAppDomainsPipelineAppId appId=" + appId); s_thisAppDomainsIsapiAppId = appId; } internal static void RemoveThisAppDomainFromUnmanagedTable() { if (Interlocked.Exchange(ref s_isThisAppDomainRemovedFromUnmanagedTable, 1) != 0) { return; } // // only notify mgdeng of this shutdown if we went through // Initialize from the there // We can also have PipelineRuntime in app domains with only // other protocols // try { if (s_thisAppDomainsIsapiAppId != null && s_ApplicationContext != IntPtr.Zero) { Debug.Trace("PipelineDomain", "Calling MgdAppDomainShutdown appId=" + s_thisAppDomainsIsapiAppId + " (AppDomainAppId=" + HttpRuntime.AppDomainAppId + ")"); UnsafeIISMethods.MgdAppDomainShutdown(s_ApplicationContext); } HttpRuntime.AddAppDomainTraceMessage(SR.GetString(SR.App_Domain_Restart)); } catch(Exception e) { if (ShouldRethrowException(e)) { throw; } } } internal static bool ShouldRethrowException(Exception ex) { return ex is NullReferenceException || ex is AccessViolationException || ex is StackOverflowException || ex is OutOfMemoryException || ex is System.Threading.ThreadAbortException; } } }