File: System\ServiceModel\Activation\ListenerAdapter.cs
Project: ndp\cdf\src\WCF\SMSvcHost\SMSvcHost.csproj (SMSvcHost)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel.Activation
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime;
    using System.Runtime.Diagnostics;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Activation.Diagnostics;
    using System.ServiceModel.Channels;
    using System.Threading;
 
    class ListenerAdapter : ListenerAdapterBase
    {
        const string SiteRootPath = "/";
        IActivationService activationService;
        bool canDispatch;
 
        // Double-checked locking pattern requires volatile for read/write synchronization
        volatile bool isOpen;
        AutoResetEvent wasConnected;
        AutoResetEvent cleanupComplete;
        AppManager appManager;
        ManualResetEvent initCompleted;
        Action<object> closeAllListenerChannelInstancesCallback;
        Action<object> launchQueueInstanceCallback;
 
        // CSDMain 190118
        // We introduce this int to indicate the current closing status
        int closingProcessStatus;
        const int ClosingProcessUnBlocked = 0;
        const int ClosingProcessBlocked = 1;
        const int ClosingProcessUnBlockedByEventOnConfigManagerDisconnected = 2;
 
        internal ListenerAdapter(IActivationService activationService)
            : base(activationService.ProtocolName)
        {
            this.activationService = activationService;
            appManager = new AppManager();
        }
 
        object ThisLock { get { return this; } }
        internal bool CanDispatch { get { return canDispatch; } }
 
 
        internal override void Open()
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::Open()");
            if (!isOpen)
            {
                lock (ThisLock)
                {
                    if (!isOpen)
                    {
                        initCompleted = new ManualResetEvent(false);
                        base.Open();
                        initCompleted.WaitOne();
                        initCompleted.Close();
                        initCompleted = null;
                        isOpen = true;
                    }
                }
            }
        }
 
        internal new void Close()
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::Close()");
            if (isOpen)
            {
                lock (ThisLock)
                {
                    if (isOpen)
                    {
                        isOpen = false;
 
                        // When calling Cleanup() in the g----ful case, we must wait for all
                        // OnApplicationPoolAllQueueInstancesStopped callbacks to fire before we can stop
                        cleanupComplete = new AutoResetEvent(false);
 
                        if (!Cleanup(true))
                        {
                            // CSDMain 190118
                            // When the Cleanup(true) returns false, it means we are requesting WAS to close all the existing worker
                            // process and give us a callback. So the current thread is blocked here.
                            // In two cases the thread can be unblocked:
                            // 1. onApplicationPoolAllQueueInstancesStopped function called. That means WAS finished its work and send
                            // us the notification. This is the normal case.
                            // 2. onConfigManagerDisconnected function called. That means there's something wrong with WAS and we should
                            // not wait for the onApplicationPoolAllQueueInstancesStopped call anymore.
                            Interlocked.Exchange(ref closingProcessStatus, ClosingProcessBlocked);
                            cleanupComplete.WaitOne(ListenerConstants.ServiceStopTimeout, false);
                            Interlocked.Exchange(ref closingProcessStatus, ClosingProcessUnBlocked);
                        }
 
                        // base.Close causes WebhostUnregisterProtocol to be called.
                        base.Close();
                    }
                }
            }
        }
 
        bool Cleanup(bool closeInstances)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::Cleanup()");
            canDispatch = false;
            bool completeSelf = true;
 
            if (closeInstances)
            {
                List<App> existingApps = new List<App>();
                List<App> removeApps = new List<App>();
                List<App> delayRemoveApps = new List<App>();
                lock (appManager)
                {
                    if (appManager.AppsCount != 0)
                    {
                        // cleanup for activation service stop: tell WAS about it
                        existingApps.AddRange(appManager.Apps.Values);
                        foreach (App app in existingApps)
                        {
                            if (app.MessageQueue.HasStartedQueueInstances)
                            {
                                delayRemoveApps.Add(app);
                            }
                            else
                            {
                                removeApps.Add(app);
                            }
                        }
 
                        existingApps.Clear();
                    }
                }
 
                if (removeApps.Count != 0)
                {
                    foreach (App app in removeApps)
                    {
                        RemoveApp(app);
                    }
                }
 
                if (delayRemoveApps.Count != 0)
                {
                    foreach (App app in delayRemoveApps)
                    {
                        if (app.PendingAction != null)
                        {
                            app.PendingAction.MergeFromDeletedAction();
                        }
                        else
                        {
                            // Create a new action
                            app.SetPendingAction(AppAction.CreateDeletedAction());
                            CloseAllListenerChannelInstances(app);
                        }
                    }
 
                    completeSelf = false;
                }
            }
            else
            {
                lock (appManager)
                {
                    appManager.Clear();
                }
            }
 
            return completeSelf;
        }
 
        void CloseAllListenerChannelInstances(App app)
        {
            int hresult = CloseAllListenerChannelInstances(app.AppPool.AppPoolId, app.MessageQueue.ListenerChannelContext.ListenerChannelId);
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::CloseAllListenerChannelInstances(" + app.AppKey + ") returned: " + hresult);
            if (hresult == 0)
            {
                if (DiagnosticUtility.ShouldTraceInformation)
                {
                    ListenerTraceUtility.TraceEvent(TraceEventType.Information, ListenerTraceCode.WasCloseAllListenerChannelInstances, SR.GetString(SR.TraceCodeWasCloseAllListenerChannelInstances), this);
                }
                if (TD.WasCloseAllListenerChannelInstancesCompletedIsEnabled())
                {
                    //WasWebHostAPI
                    TD.WasCloseAllListenerChannelInstancesCompleted(this.EventTraceActivity);
                }
            }
            else
            {
                if (DiagnosticUtility.ShouldTraceError)
                {
                    ListenerTraceUtility.TraceEvent(TraceEventType.Error, ListenerTraceCode.WasWebHostAPIFailed, SR.GetString(SR.TraceCodeWasWebHostAPIFailed),
                        new StringTraceRecord("HRESULT", SR.GetString(SR.TraceCodeWasWebHostAPIFailed,
                        "WebhostCloseAllListenerChannelInstances", hresult.ToString(CultureInfo.CurrentCulture))), this, null);
                }
                if (TD.WasCloseAllListenerChannelInstancesFailedIsEnabled())
                {
                    //WasWebHostAPIFailed
                    TD.WasCloseAllListenerChannelInstancesFailed(this.EventTraceActivity, hresult.ToString(CultureInfo.CurrentCulture));
                }
            }
        }
 
        protected override void OnApplicationAppPoolChanged(string appKey, string appPoolId)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationAppPoolChanged(" + appKey + ", " + appPoolId + ")");
 
            try
            {
                App app = null;
                lock (appManager)
                {
                    // The app might have been removed due to the service shutdown.
                    if (!appManager.Apps.TryGetValue(appKey, out app))
                    {
                        return;
                    }
                }
 
                if (app.PendingAction != null)
                {
                    app.PendingAction.MergeFromAppPoolChangedAction(appPoolId);
                }
                else
                {
                    if (app.MessageQueue.HasStartedQueueInstances)
                    {
                        // Create a new action
                        app.SetPendingAction(AppAction.CreateAppPoolChangedAction(appPoolId));
                        ScheduleClosingListenerChannelInstances(app);
                    }
                    else
                    {
                        CompleteAppPoolChange(app, appPoolId);
                    }
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        internal void CompleteAppPoolChange(App app, string appPoolId)
        {
            lock (appManager)
            {
                AppPool appPool;
                if (!appManager.AppPools.TryGetValue(appPoolId, out appPool))
                {
                    // The AppPool has been removed
                    return;
                }
 
                if (appPool != app.AppPool)
                {
                    app.AppPool.RemoveApp(app);
                    appPool.AddApp(app);
                    app.OnAppPoolChanged(appPool);
                }
            }
        }
 
        protected override void OnApplicationDeleted(string appKey)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationDeleted(" + appKey + ")");
 
            try
            {
                App app = null;
                lock (appManager)
                {
                    // CSDMain 190118
                    // In some cases WAS will send us duplicated notification for the deletion of a same appKey
                    if (!appManager.Apps.ContainsKey(appKey))
                    {
                        return;
                    }
                    app = appManager.Apps[appKey];
                }
 
                if (app.PendingAction != null)
                {
                    app.PendingAction.MergeFromDeletedAction();
                }
                else
                {
                    if (app.MessageQueue.HasStartedQueueInstances)
                    {
                        // Creae a new action
                        app.SetPendingAction(AppAction.CreateDeletedAction());
                        ScheduleClosingListenerChannelInstances(app);
                    }
                    else
                    {
                        CompleteDeleteApp(app);
                    }
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        internal void CompleteDeleteApp(App app)
        {
            RemoveApp(app);
        }
 
        void CompleteAppSettingsChanged(App app)
        {
            if (app.PendingAction.AppPoolId != null)
            {
                CompleteAppPoolChange(app, app.PendingAction.AppPoolId);
            }
 
            if (app.PendingAction.Path != null)
            {
                app.Path = app.PendingAction.Path;
            }
 
            if (app.PendingAction.Bindings != null)
            {
                RegisterNewBindings(app, app.PendingAction.Bindings);
            }
 
            if (app.PendingAction.RequestsBlocked.HasValue)
            {
                app.SetRequestBlocked(app.PendingAction.RequestsBlocked.Value);
            }
        }
 
        void RemoveApp(App app)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::RemoveApp(" + app.AppKey + ")");
 
            lock (appManager)
            {
                // The App might have been deleted when the AppPool is deleted.
                if (appManager.Apps.ContainsKey(app.AppKey))
                {
                    appManager.DeleteApp(app, false);
                }
            }
        }
 
        protected override void OnApplicationBindingsChanged(string appKey, IntPtr bindingsMultiSz, int numberOfBindings)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationBindingsChanged(" + appKey + ")");
            string[] bindings = null;
            try
            {
                bindings = base.ParseBindings(bindingsMultiSz, numberOfBindings);
            }
            catch (ArgumentException exception)
            {
                DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
 
                // Ignore the binding change if WAS provides wrong bindings.
                return;
            }
 
            App app = null;
            lock (appManager)
            {
                // The app might have been removed due to the service shutdown.
                if (!appManager.Apps.TryGetValue(appKey, out app))
                {
                    return;
                }
            }
 
            try
            {
                if (app.PendingAction != null)
                {
                    app.PendingAction.MergeFromBindingChangedAction(bindings);
                }
                else
                {
                    if (app.MessageQueue.HasStartedQueueInstances)
                    {
                        app.SetPendingAction(AppAction.CreateBindingsChangedAction(bindings));
                        ScheduleClosingListenerChannelInstances(app);
                    }
                    else
                    {
                        RegisterNewBindings(app, bindings);
                    }
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        internal void RegisterNewBindings(App app, string[] bindings)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::RegisterNewBindings(" + app.AppKey + ")");
            // we could be smart-er and leave the bindings that have not changed alone
 
            app.MessageQueue.UnregisterAll();
 
            bool success = RegisterBindings(app.MessageQueue, app.SiteId, bindings, app.Path);
            app.OnInvalidBinding(!success);
        }
 
        protected override void OnApplicationCreated(string appKey, string path, int siteId, string appPoolId, IntPtr bindingsMultiSz, int numberOfBindings, bool requestsBlocked)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationCreated(" + appKey + ", " + path + ", " + siteId + ", " + appPoolId + ", " + requestsBlocked + ")");
            string[] bindings = null;
            try
            {
                bindings = base.ParseBindings(bindingsMultiSz, numberOfBindings);
            }
            catch (ArgumentException exception)
            {
                DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
 
                // Ignore the app if WAS provides wrong bindings.
                return;
            }
 
            try
            {
                bool found = true;
                App app = null;
 
                lock (appManager)
                {
                    if (!appManager.Apps.TryGetValue(appKey, out app))
                    {
                        found = false;
                        app = appManager.CreateApp(appKey, path, siteId, appPoolId, requestsBlocked);
                    }
                }
 
                if (found)
                {
                    Fx.Assert(app.PendingAction != null, "The app should be waiting for AllLCStopped notification.");
                    app.PendingAction.MergeFromCreatedAction(path, siteId, appPoolId, requestsBlocked, bindings);
                }
                else
                {
                    CompleteAppCreation(app, bindings);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        void CompleteAppCreation(App app, string[] bindings)
        {
            IActivatedMessageQueue queue = activationService.CreateQueue(this, app);
            app.RegisterQueue(queue);
 
            bool success = RegisterBindings(app.MessageQueue, app.SiteId, bindings, app.Path);
            app.OnInvalidBinding(!success);
        }
 
        bool RegisterBindings(IActivatedMessageQueue queue, int siteId, string[] bindings, string path)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::RegisterBindings() bindings#: " + bindings.Length);
            BaseUriWithWildcard[] baseAddresses = new BaseUriWithWildcard[bindings.Length];
            // first make sure all the bindings are valid for this protocol
            for (int i = 0; i < bindings.Length; i++)
            {
                string binding = bindings[i];
                int index = binding.IndexOf(':');
                string protocol = binding.Substring(0, index);
                if (string.Compare(this.ProtocolName, protocol, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        SR.GetString(SR.LAProtocolMismatch, protocol, path, this.ProtocolName)));
                }
 
                binding = binding.Substring(index + 1);
                try
                {
                    baseAddresses[i] = BaseUriWithWildcard.CreateHostedUri(ProtocolName, binding, path);
                    Debug.Print("ListenerAdapter[" + ProtocolName + "]::RegisterBindings() CreateUrlFromBinding(binding: " + binding + " path: " + path + ") returned baseAddress: " + baseAddresses[i]);
                }
                catch (UriFormatException exception)
                {
                    Debug.Print("ListenerAdapter[" + ProtocolName + "]::RegisterBindings() CreateUrlFromBinding(binding: " + binding + " path: " + path + ") failed with UriFormatException: " + exception.Message);
                    DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
 
                    // We only log the event for the site root.
                    if (string.Compare(path, SiteRootPath, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        DiagnosticUtility.EventLog.LogEvent(TraceEventType.Error,
                            (ushort)EventLogCategory.ListenerAdapter,
                            (uint)EventLogEventId.BindingError,
                            protocol,
                            binding,
                            siteId.ToString(NumberFormatInfo.CurrentInfo),
                            bindings[i],
                            ListenerTraceUtility.CreateSourceString(this),
                            exception.ToString());
                    }
 
                    return false;
                }
            }
 
            // now make sure all the bindings can be listened on or roll back
            for (int i = 0; i < bindings.Length; i++)
            {
                ListenerExceptionStatus status = ListenerExceptionStatus.FailedToListen;
                Exception exception = null;
                try
                {
                    status = queue.Register(baseAddresses[i]);
                    Debug.Print("ListenerAdapter[" + ProtocolName + "]::RegisterBindings() registering baseAddress: " + baseAddresses[i] + " with queue returned: " + status);
                }
                catch (Exception ex)
                {
                    if (Fx.IsFatal(ex))
                    {
                        throw;
                    }
 
                    DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
 
                    exception = ex;
                }
 
                if (status != ListenerExceptionStatus.Success)
                {
                    // We only log the event for the site root.
                    if (string.Compare(path, SiteRootPath, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        DiagnosticUtility.EventLog.LogEvent(TraceEventType.Error,
                            (ushort)EventLogCategory.ListenerAdapter,
                            (uint)EventLogEventId.LAFailedToListenForApp,
                            activationService.ActivationServiceName,
                            ProtocolName,
                            siteId.ToString(NumberFormatInfo.CurrentInfo),
                            baseAddresses[i].ToString(),
                            status.ToString(),
                            exception == null ? string.Empty : exception.ToString());
                    }
 
                    queue.UnregisterAll();
                    return false;
                }
            }
 
            return true;
        }
 
        protected override void OnApplicationPoolAllQueueInstancesStopped(string appPoolId, int queueId)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationPoolAllQueueInstancesStopped(" + appPoolId + ", " + queueId + ")");
 
            if (!isOpen)
            {
                if (closingProcessStatus != ClosingProcessBlocked)
                {
                    // OnConfigManagerDisconnected was already called. We should just return.
                    return;
                }
            }
 
            try
            {
                AppPool appPool = null;
                lock (appManager)
                {
                    if (!appManager.AppPools.TryGetValue(appPoolId, out appPool))
                    {
                        // Ignore this notification if we received OnApplicationPoolDeleted.                
                        return;
                    }
                }
 
                IActivatedMessageQueue queue = activationService.FindQueue(queueId);
                if (queue == null)
                {
                    // This is the belated notification. Ignore it.
                    return;
                }
 
                Fx.Assert(queue.App.AppPool.AppPoolId == appPoolId, "OnApplicationPoolAllQueueInstancesStopped: unexpected pool id");
                queue.OnQueueInstancesStopped();
 
                App app = queue.App;
 
                try
                {
                    if (app.PendingAction.ActionType == AppActionType.Deleted)
                    {
                        CompleteDeleteApp(app);
                        SignalCleanupForNoApps();
                    }
                    else if (app.PendingAction.ActionType == AppActionType.SettingsChanged)
                    {
                        CompleteAppSettingsChanged(app);
                    }
                }
                finally
                {
                    // Reset the action
                    app.SetPendingAction(null);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnApplicationPoolCanLaunchQueueInstance(string appPoolId, int queueId)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationPoolCanLaunchQueueInstance(" + appPoolId + ", " + queueId + ")");
 
            try
            {
                IActivatedMessageQueue queue = activationService.FindQueue(queueId);
                if (queue != null)
                {
                    if (queue.App.AppPool.AppPoolId != appPoolId)
                    {
                        throw Fx.AssertAndThrow("OnApplicationPoolCanLaunchQueueInstance: unexpected pool id");
                    }
 
                    ScheduleLaunchingQueueInstance(queue);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnApplicationPoolCreated(string appPoolId, SecurityIdentifier sid)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationPoolCreated(" + appPoolId + ", " + sid + ")");
 
            try
            {
                lock (appManager)
                {
                    appManager.CreateAppPool(appPoolId, sid);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnApplicationPoolDeleted(string appPoolId)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationPoolDeleted(" + appPoolId + ")");
 
            try
            {
                lock (appManager)
                {
                    appManager.DeleteAppPool(appPoolId);
                }
 
                SignalCleanupForNoApps();
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnApplicationPoolIdentityChanged(string appPoolId, SecurityIdentifier sid)
        {
            try
            {
                Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationPoolIdentityChanged(" + appPoolId + ", " + sid + ")");
 
                AppPool appPool = null;
                lock (appManager)
                {
                    if (!appManager.AppPools.TryGetValue(appPoolId, out appPool))
                    {
                        return;
                    }
 
                    appPool.SetIdentity(sid);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnApplicationPoolStateChanged(string appPoolId, bool isEnabled)
        {
            try
            {
                Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationPoolStateChanged(" + appPoolId + ", " + isEnabled + ")");
                AppPool appPool = null;
                lock (appManager)
                {
                    if (!appManager.AppPools.TryGetValue(appPoolId, out appPool))
                    {
                        return;
                    }
 
                    appPool.SetEnabledState(isEnabled);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnApplicationRequestsBlockedChanged(string appKey, bool requestsBlocked)
        {
            try
            {
                Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnApplicationRequestsBlockedChanged(" + appKey + ", " + requestsBlocked + ")");
                App app = null;
 
                lock (appManager)
                {
                    if (!appManager.Apps.TryGetValue(appKey, out app))
                    {
                        return;
                    }
                }
 
                if (app.PendingAction != null)
                {
                    app.PendingAction.MergeFromRequestsBlockedAction(requestsBlocked);
                }
                else
                {
                    app.SetRequestBlocked(requestsBlocked);
                }
            }
            catch (Exception exception)
            {
                HandleUnknownError(exception);
            }
        }
 
        protected override void OnConfigManagerConnected()
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnConfigManagerConnected()");
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                ListenerTraceUtility.TraceEvent(TraceEventType.Information, ListenerTraceCode.WasConnected, SR.GetString(SR.TraceCodeWasConnected), this);
            }
            if (TD.WasConnectedIsEnabled())
            {
                TD.WasConnected(this.EventTraceActivity);
            }
 
            if (wasConnected != null)
            {
                wasConnected.Set();
            }
        }
 
        protected override void OnConfigManagerDisconnected(int hresult)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnConfigManagerDisconnected(" + hresult + ") isOpen: " + isOpen);
            if (!isOpen)
            {
                int currentClosingProcessStatus = Interlocked.CompareExchange(ref closingProcessStatus, 
                                                                    ClosingProcessUnBlockedByEventOnConfigManagerDisconnected,
                                                                    ClosingProcessBlocked);
                if (currentClosingProcessStatus == ClosingProcessBlocked)
                {
                    // CSDMain 190118
                    // According to WAS team, if we receive this call before the call "OnApplicationPoolAllQueueInstancesStopped",
                    // then we will not receive the call "OnApplicationPoolAllQueueInstancesStopped" ever. So in this case we should
                    // do cleanup on our side directly.
                    Cleanup(false);
                    SignalCleanupForNoApps();
                }
                return;
            }
 
            if (TD.WasDisconnectedIsEnabled())
            {
                TD.WasDisconnected(this.EventTraceActivity);
            }
 
            DiagnosticUtility.EventLog.LogEvent(TraceEventType.Warning,
                (ushort)EventLogCategory.ListenerAdapter,
                (uint)EventLogEventId.WasDisconnected,
                hresult.ToString(CultureInfo.InvariantCulture));
 
            // WAS has crashed.
            Cleanup(false);
            wasConnected = new AutoResetEvent(false);
            ThreadPool.UnsafeRegisterWaitForSingleObject(wasConnected, Fx.ThunkCallback(new WaitOrTimerCallback(WasConnected)), null, ListenerConstants.WasConnectTimeout, true);
        }
 
        void WasConnected(object state, bool timedOut)
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::WasConnected() timedOut: " + timedOut);
            if (timedOut)
            {
                // WAS didn't connect within the timeout give up waiting and stop
                DiagnosticUtility.EventLog.LogEvent(TraceEventType.Warning,
                    (ushort)EventLogCategory.ListenerAdapter,
                    (uint)EventLogEventId.WasConnectionTimedout);
                
                if (TD.WasConnectionTimedoutIsEnabled())
                {
                    TD.WasConnectionTimedout(this.EventTraceActivity);
                }
 
                activationService.StopService();
 
            }
            wasConnected.Close();
            wasConnected = null;
        }
 
        protected override void OnConfigManagerInitializationCompleted()
        {
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::OnConfigManagerInitializationCompleted()");
            canDispatch = true;
 
            ManualResetEvent initCompleted = this.initCompleted;
            if (initCompleted != null)
            {
                initCompleted.Set();
            }
        }
 
        internal bool OpenListenerChannelInstance(IActivatedMessageQueue queue)
        {
            byte[] queueBlob = queue.ListenerChannelContext.Dehydrate();
            Debug.Print("ListenerAdapter[" + ProtocolName + "]::ListenerAdapter.OpenListenerChannelInstance(appPoolId:" + queue.App.AppPool.AppPoolId + " appKey:" + queue.ListenerChannelContext.AppKey + " queueId:" + queue.ListenerChannelContext.ListenerChannelId + ")");
            int hresult = OpenListenerChannelInstance(queue.App.AppPool.AppPoolId, queue.ListenerChannelContext.ListenerChannelId, queueBlob);
            if (hresult != 0)
            {
                if (DiagnosticUtility.ShouldTraceError)
                {
                    ListenerTraceUtility.TraceEvent(TraceEventType.Error, ListenerTraceCode.WasWebHostAPIFailed, SR.GetString(SR.TraceCodeWasWebHostAPIFailed),
                        new StringTraceRecord("HRESULT", SR.GetString(SR.TraceCodeWasWebHostAPIFailed,
                        "WebhostOpenListenerChannelInstance", hresult.ToString(CultureInfo.CurrentCulture))), this, null);
                }
                if (TD.OpenListenerChannelInstanceFailedIsEnabled())
                {
                    TD.OpenListenerChannelInstanceFailed(this.EventTraceActivity, hresult.ToString(CultureInfo.CurrentCulture));
                }
 
                return false;
            }
 
            return true;
        }
 
        void ScheduleClosingListenerChannelInstances(App app)
        {
            if (closeAllListenerChannelInstancesCallback == null)
            {
                closeAllListenerChannelInstancesCallback = new Action<object>(OnCloseAllListenerChannelInstances);
            }
 
            ActionItem.Schedule(closeAllListenerChannelInstancesCallback, app);
        }
 
        void OnCloseAllListenerChannelInstances(object state)
        {
            App app = state as App;
            Fx.Assert(app != null, "OnCloseAllListenerChannelInstances: app is null");
 
            CloseAllListenerChannelInstances(app);
        }
 
        void ScheduleLaunchingQueueInstance(IActivatedMessageQueue queue)
        {
            if (launchQueueInstanceCallback == null)
            {
                launchQueueInstanceCallback = new Action<object>(OnLaunchQueueInstance);
            }
 
            ActionItem.Schedule(launchQueueInstanceCallback, queue);
        }
 
        void OnLaunchQueueInstance(object state)
        {
            IActivatedMessageQueue queue = state as IActivatedMessageQueue;
            Fx.Assert(queue != null, "OnLaunchQueueInstance: queue is null");
 
            queue.LaunchQueueInstance();
        }
 
        void HandleUnknownError(Exception exception)
        {
            DiagnosticUtility.EventLog.LogEvent(TraceEventType.Error,
                (ushort)EventLogCategory.ListenerAdapter,
                (uint)EventLogEventId.UnknownListenerAdapterError,
                this.ProtocolName,
                exception.ToString());
 
            if (TD.FailFastExceptionIsEnabled())
            {
                TD.FailFastException(this.EventTraceActivity, exception);
            }
            // We cannot handle this exception and thus have to terminate the process.
            DiagnosticUtility.InvokeFinalHandler(exception);
        }
 
        void SignalCleanupForNoApps()
        {
            if (this.cleanupComplete != null)
            {
                lock (appManager)
                {
                    if (appManager.AppsCount == 0)
                    {
                        // on g----ful cleanup we need to unblock Close() when no app is left running
                        this.appManager.Clear();
                        this.cleanupComplete.Set();
                    }
                }
            }
        }
    }
}