/** Remove the counters for the given job group. */
 public void removeByGroupCounters(TaskTimer timer, QueueJobGroup group) {
   timer.acquire();
   synchronized (pCountersByGroup) {
     timer.resume();
     pCountersByGroup.remove(group.getGroupID());
   }
 }
  /** Get the distribution of job states for the jobs in the given group. */
  public double[] getDistribution(TaskTimer timer, long groupID) {
    Counters counters = null;
    timer.acquire();
    synchronized (pCountersByGroup) {
      timer.resume();
      counters = pCountersByGroup.get(groupID);
    }

    if (counters != null) {
      double dist[] = counters.distribution();

      //       if(LogMgr.getInstance().isLoggable(LogMgr.Kind.Ops, LogMgr.Level.Finest)) {
      //         StringBuilder buf = new StringBuilder();
      //         buf.append("Job Group Distribution [" + groupID + "]:\n  ");
      //         for(JobState js : JobState.all())
      //           buf.append(js + "[" + String.format("%1$.4f", dist[js.ordinal()]) + "] ");
      // 	LogMgr.getInstance().log(LogMgr.Kind.Ops, LogMgr.Level.Finest, buf.toString());
      //       }

      return dist;
    } else {
      LogMgr.getInstance()
          .logAndFlush(
              LogMgr.Kind.Ops,
              LogMgr.Level.Warning,
              "Somehow the job group (" + groupID + ") was not in the state counts table!");

      return new double[JobState.all().size()];
    }
  }
  /**
   * Get the percentage of jobs in the job group owning the given job which are currently Queued or
   * Preempted.
   */
  public double percentPending(TaskTimer timer, long jobID) {
    Counters counters = null;
    timer.acquire();
    synchronized (pCountersByJob) {
      timer.resume();
      counters = pCountersByJob.get(jobID);
    }

    if (counters != null) {
      double percent = counters.percentPending();

      //       if(LogMgr.getInstance().isLoggable(LogMgr.Kind.Ops, LogMgr.Level.Finest))
      //         LogMgr.getInstance().log
      //           (LogMgr.Kind.Ops, LogMgr.Level.Finest,
      //            "Percent Pending [" + jobID + "]: " + String.format("%1$.4f", percent));

      return percent;
    } else {
      LogMgr.getInstance()
          .logAndFlush(
              LogMgr.Kind.Ops,
              LogMgr.Level.Warning,
              "Somehow the job (" + jobID + ") was not in the state counts table!");
      return 0.0;
    }
  }
  /** Remove the counters for the given jobs which are no longer referenced by any job group. */
  public void removeByJobCounters(TaskTimer timer, Set<Long> jobIDs) {
    timer.acquire();
    synchronized (pCountersByJob) {
      timer.resume();

      for (Long jobID : jobIDs) pCountersByJob.remove(jobID);
    }
  }
  /**
   * Update the job state counts after a change to the job state information.
   *
   * @param timer The operation timer.
   * @param prevState The previous job state before the change or <CODE>null</CODE> if the previous
   *     state is unknown.
   * @para info The current job info which includes the updated job state.
   */
  public void update(TaskTimer timer, JobState prevState, QueueJobInfo info) {
    Counters counters = null;
    timer.acquire();
    synchronized (pCountersByJob) {
      timer.resume();
      counters = pCountersByJob.get(info.getJobID());
    }

    if (counters != null) counters.update(prevState, info);
    else
      LogMgr.getInstance()
          .logAndFlush(
              LogMgr.Kind.Ops,
              LogMgr.Level.Warning,
              "Somehow the job (" + info.getJobID() + ") was not in the state counts table!");
  }
  /** Get the percentage of jobs in the job group which are currently Queued or Preempted. */
  public double percentPendingByGroup(TaskTimer timer, long jobGroupID) {
    Counters counters = null;
    timer.acquire();
    synchronized (pCountersByGroup) {
      timer.resume();
      counters = pCountersByGroup.get(jobGroupID);
    }

    if (counters != null) {
      double percent = counters.percentPending();

      return percent;
    } else {
      LogMgr.getInstance()
          .logAndFlush(
              LogMgr.Kind.Ops,
              LogMgr.Level.Warning,
              "Somehow the job group (" + jobGroupID + ") was not in the state counts table!");
      return 0.0;
    }
  }
  /** Create a new set of shared job state counters for all jobs in the given group. */
  public void initCounters(TaskTimer timer, QueueJobGroup group) {
    long groupID = group.getGroupID();

    if (LogMgr.getInstance().isLoggable(LogMgr.Kind.Ops, LogMgr.Level.Finest))
      LogMgr.getInstance()
          .log(LogMgr.Kind.Ops, LogMgr.Level.Finest, "Init Job Counts for Group (" + groupID + ")");

    SortedSet<Long> jobIDs = group.getJobIDs();
    Counters counters = new Counters(jobIDs.size());

    timer.acquire();
    synchronized (pCountersByGroup) {
      timer.resume();

      if (pCountersByGroup.put(groupID, counters) != null)
        LogMgr.getInstance()
            .logAndFlush(
                LogMgr.Kind.Ops,
                LogMgr.Level.Warning,
                "Somehow the job group ("
                    + groupID
                    + ") was already in the state "
                    + "counts table!");
    }

    timer.acquire();
    synchronized (pCountersByJob) {
      timer.resume();

      for (Long jobID : jobIDs) {
        if (pCountersByJob.put(jobID, counters) != null)
          LogMgr.getInstance()
              .logAndFlush(
                  LogMgr.Kind.Ops,
                  LogMgr.Level.Warning,
                  "Somehow the job (" + jobID + ") was already in the state counts table!");
      }
    }
  }