/// <summary>
        /// Checks to see whether any other applications runnable now that the given
        /// application has been removed from the given queue.
        /// </summary>
        /// <remarks>
        /// Checks to see whether any other applications runnable now that the given
        /// application has been removed from the given queue.  And makes them so.
        /// Runs in O(n log(n)) where n is the number of queues that are under the
        /// highest queue that went from having no slack to having slack.
        /// </remarks>
        public virtual void UpdateRunnabilityOnAppRemoval(FSAppAttempt app, FSLeafQueue queue
                                                          )
        {
            AllocationConfiguration allocConf = scheduler.GetAllocationConfiguration();
            // childqueueX might have no pending apps itself, but if a queue higher up
            // in the hierarchy parentqueueY has a maxRunningApps set, an app completion
            // in childqueueX could allow an app in some other distant child of
            // parentqueueY to become runnable.
            // An app removal will only possibly allow another app to become runnable if
            // the queue was already at its max before the removal.
            // Thus we find the ancestor queue highest in the tree for which the app
            // that was at its maxRunningApps before the removal.
            FSQueue highestQueueWithAppsNowRunnable = (queue.GetNumRunnableApps() == allocConf
                                                       .GetQueueMaxApps(queue.GetName()) - 1) ? queue : null;
            FSParentQueue parent = queue.GetParent();

            while (parent != null)
            {
                if (parent.GetNumRunnableApps() == allocConf.GetQueueMaxApps(parent.GetName()) -
                    1)
                {
                    highestQueueWithAppsNowRunnable = parent;
                }
                parent = parent.GetParent();
            }
            IList <IList <FSAppAttempt> > appsNowMaybeRunnable = new AList <IList <FSAppAttempt> >(
                );

            // Compile lists of apps which may now be runnable
            // We gather lists instead of building a set of all non-runnable apps so
            // that this whole operation can be O(number of queues) instead of
            // O(number of apps)
            if (highestQueueWithAppsNowRunnable != null)
            {
                GatherPossiblyRunnableAppLists(highestQueueWithAppsNowRunnable, appsNowMaybeRunnable
                                               );
            }
            string user           = app.GetUser();
            int    userNumRunning = usersNumRunnableApps[user];

            if (userNumRunning == null)
            {
                userNumRunning = 0;
            }
            if (userNumRunning == allocConf.GetUserMaxApps(user) - 1)
            {
                IList <FSAppAttempt> userWaitingApps = usersNonRunnableApps.Get(user);
                if (userWaitingApps != null)
                {
                    appsNowMaybeRunnable.AddItem(userWaitingApps);
                }
            }
            UpdateAppsRunnability(appsNowMaybeRunnable, appsNowMaybeRunnable.Count);
        }
        /// <summary>
        /// Tracks the given new runnable app for purposes of maintaining max running
        /// app limits.
        /// </summary>
        public virtual void TrackRunnableApp(FSAppAttempt app)
        {
            string      user  = app.GetUser();
            FSLeafQueue queue = ((FSLeafQueue)app.GetQueue());
            // Increment running counts for all parent queues
            FSParentQueue parent = queue.GetParent();

            while (parent != null)
            {
                parent.IncrementRunnableApps();
                parent = parent.GetParent();
            }
            int userNumRunnable = usersNumRunnableApps[user];

            usersNumRunnableApps[user] = (userNumRunnable == null ? 0 : userNumRunnable) + 1;
        }
        /// <summary>
        /// Checks to see whether applications are runnable now by iterating
        /// through each one of them and check if the queue and user have slack
        /// if we know how many apps can be runnable, there is no need to iterate
        /// through all apps, maxRunnableApps is used to break out of the iteration
        /// </summary>
        private void UpdateAppsRunnability(IList <IList <FSAppAttempt> > appsNowMaybeRunnable
                                           , int maxRunnableApps)
        {
            // Scan through and check whether this means that any apps are now runnable
            IEnumerator <FSAppAttempt> iter = new MaxRunningAppsEnforcer.MultiListStartTimeIterator
                                                  (appsNowMaybeRunnable);
            FSAppAttempt         prev = null;
            IList <FSAppAttempt> noLongerPendingApps = new AList <FSAppAttempt>();

            while (iter.HasNext())
            {
                FSAppAttempt next = iter.Next();
                if (next == prev)
                {
                    continue;
                }
                if (CanAppBeRunnable(((FSLeafQueue)next.GetQueue()), next.GetUser()))
                {
                    TrackRunnableApp(next);
                    FSAppAttempt appSched = next;
                    ((FSLeafQueue)next.GetQueue()).AddApp(appSched, true);
                    noLongerPendingApps.AddItem(appSched);
                    if (noLongerPendingApps.Count >= maxRunnableApps)
                    {
                        break;
                    }
                }
                prev = next;
            }
            // We remove the apps from their pending lists afterwards so that we don't
            // pull them out from under the iterator.  If they are not in these lists
            // in the first place, there is a bug.
            foreach (FSAppAttempt appSched_1 in noLongerPendingApps)
            {
                if (!((FSLeafQueue)appSched_1.GetQueue()).RemoveNonRunnableApp(appSched_1))
                {
                    Log.Error("Can't make app runnable that does not already exist in queue" + " as non-runnable: "
                              + appSched_1 + ". This should never happen.");
                }
                if (!usersNonRunnableApps.Remove(appSched_1.GetUser(), appSched_1))
                {
                    Log.Error("Waiting app " + appSched_1 + " expected to be in " + "usersNonRunnableApps, but was not. This should never happen."
                              );
                }
            }
        }
        /// <summary>
        /// Updates the relevant tracking variables after a runnable app with the given
        /// queue and user has been removed.
        /// </summary>
        public virtual void UntrackRunnableApp(FSAppAttempt app)
        {
            // Update usersRunnableApps
            string user = app.GetUser();
            int    newUserNumRunning = usersNumRunnableApps[user] - 1;

            if (newUserNumRunning == 0)
            {
                Sharpen.Collections.Remove(usersNumRunnableApps, user);
            }
            else
            {
                usersNumRunnableApps[user] = newUserNumRunning;
            }
            // Update runnable app bookkeeping for queues
            FSLeafQueue   queue  = ((FSLeafQueue)app.GetQueue());
            FSParentQueue parent = queue.GetParent();

            while (parent != null)
            {
                parent.DecrementRunnableApps();
                parent = parent.GetParent();
            }
        }
        /// <summary>
        /// Tracks the given new non runnable app so that it can be made runnable when
        /// it would not violate max running app limits.
        /// </summary>
        public virtual void TrackNonRunnableApp(FSAppAttempt app)
        {
            string user = app.GetUser();

            usersNonRunnableApps.Put(user, app);
        }
 /// <summary>Stops tracking the given non-runnable app</summary>
 public virtual void UntrackNonRunnableApp(FSAppAttempt app)
 {
     usersNonRunnableApps.Remove(app.GetUser(), app);
 }