Source: js/suspending_app_priority_manager.js

'use strict';

(function(window) {
  /**
   * The module's responsibility is to actively kill
   * a suspending app window according to launch time if there're
   * too many suspending app window instances running.
   *
   * About suspending: if an app is killed by OOM killer,
   * it would remain in the task manager if 'app-suspending.enabled'
   * is turned on.
   *
   * It depends on the app stack managed by StackManager.
   *
   * @module  SuspendingAppPriorityManager
   * @requires module:StackManager
   * @requires module:System
   */
  var SuspendingAppPriorityManager = function() {};
  SuspendingAppPriorityManager.prototype.start = function() {
    window.addEventListener('appsuspended', this);
    window.addEventListener('appresumed', this);
  };

  /**
   * The current suspending app count.
   */
  SuspendingAppPriorityManager.prototype.suspendedCount = 0;
  /**
   * The maximum suspending app count we could suffer.
   * @type {Number}
   */
  SuspendingAppPriorityManager.prototype.MAXIMUM_SUSPENDED_COUNT = 10;
  SuspendingAppPriorityManager.prototype._DEBUG = false;

  SuspendingAppPriorityManager.prototype.handleEvent = function(evt) {
    switch (evt.type) {
      case 'appsuspended':
        this.suspendedCount++;
        this.debug('suspending app count increases to ' + this.suspendedCount);
        this._handleSuspendingAppCountChanged();
        break;
      case 'appresumed':
        this.suspendedCount--;
        this.debug('suspending app count decreases to ' + this.suspendedCount);
        break;
    }
  };

  /**
   * Check if the suspending app count exceeds the maximum;
   * if so, find an eldest suspending app to kill.
   */
  SuspendingAppPriorityManager.prototype.
    _handleSuspendingAppCountChanged = function() {
      if (this.suspendedCount > this.MAXIMUM_SUSPENDED_COUNT) {
        this.debug('exceed maximum suspending count, find someone to kill..');
        // XXX: Maintain our own suspending app stack.
        var stack = self.StackManager._stack;
        var current = self.StackManager._current;
        this.debug(stack.length, current);
        // Go through the stack to find the eldest app which is suspended.
        for (var i = current; i < current + stack.length; i++) {
          var index = i % stack.length;
          var candidate = stack[index];
          this.debug(index, candidate.name, candidate.suspended);
          if (candidate.suspended) {
            this.debug('killing zombie app: ' + candidate.name);
            candidate.kill();
            return;
          }
        }
      }
    };

  SuspendingAppPriorityManager.prototype.debug = function() {
    if (this._DEBUG) {
      console.log('[SuspendingAppPriorityManager]' +
        '[' + self.Service.currentTime() + '] ' +
        Array.slice(arguments).concat());
    }
  };

  window.SuspendingAppPriorityManager = SuspendingAppPriorityManager;
}(self));