/**
  * Main process loop, takes a runnable task and executes it. If the graph has not been built when
  * getDependencyGraph is called, the calling thread will also join this. There are additional
  * threads that also run in a pool to complete the work of the graph building.
  *
  * @param context the calling thread's building context
  * @return true if there is more work still to do, false if all the work is done
  */
 protected boolean buildGraph(final GraphBuildingContext context) {
   ContextRunnable task = _runQueue.take();
   if (task == null) {
     task = _deferredQueue.poll();
     if (task == null) {
       // Nothing runnable and nothing deferred
       return false;
     }
   }
   if (!task.tryRun(context)) {
     // A concurrency limit was hit. Post the job into the contention buffer and try and take
     // another from the run queue.
     do {
       _deferredQueue.add(task);
       task = _runQueue.take();
       if (task == null) {
         // Nothing runnable. Abort as we can't just add the deferred items or we might end up
         // spinning
         return false;
       }
     } while (!task.tryRun(context));
   }
   // Reschedule a deferred item. There may be multiple deferred items, but we only release them
   // one at a time as other jobs complete
   // which may resolve the contention. If the run queue becomes empty they will be taken directly
   // by the code above.
   task = _deferredQueue.poll();
   if (task != null) {
     addToRunQueue(task);
   }
   _completedSteps.incrementAndGet();
   return true;
 }
 /**
  * If there are runnable tasks but not as many active jobs as the requested number then additional
  * threads will be started. This is called when the number of background threads is changed.
  */
 protected void startBackgroundBuild() {
   if (_runQueue.isEmpty()) {
     s_logger.info("No pending runnable tasks for background building");
   } else {
     final Iterator<ContextRunnable> itr = _runQueue.iterator();
     while (itr.hasNext() && startBackgroundConstructionJob()) {
       itr.next();
     }
   }
 }
 protected boolean isGraphBuilt(final boolean allowBackgroundContinuation)
     throws InterruptedException {
   if (!isGraphBuilt()) {
     s_logger.info("Building dependency graph");
     do {
       final Job job = createConstructionJob();
       _activeJobCount.incrementAndGet();
       synchronized (_activeJobs) {
         if (!_cancelled) {
           _activeJobs.add(job);
         } else {
           throw new CancellationException();
         }
       }
       job.run();
       synchronized (_activeJobs) {
         if (!_runQueue.isEmpty()) {
           // more jobs in the queue so keep going
           continue;
         }
       }
       if (allowBackgroundContinuation) {
         // Nothing in the queue for us so take a nap. There are background threads running and
         // maybe items on the deferred queue.
         s_logger.info("Waiting for background threads");
         Thread.sleep(100);
       } else {
         return false;
       }
     } while (!isGraphBuilt());
   }
   return true;
 }
 protected void reportStateSize() {
   _getTerminalValuesCallback.reportStateSize();
   if (!s_logger.isInfoEnabled()) {
     return;
   }
   int count = 0;
   for (final Map<ResolveTask, ResolveTask> entries : _requirements.values()) {
     synchronized (entries) {
       count += entries.size();
     }
   }
   s_logger.info("Requirements cache = {} tasks for {} requirements", count, _requirements.size());
   count = 0;
   for (final MapEx<ResolveTask, ResolvedValueProducer> entries : _specifications.values()) {
     synchronized (entries) {
       count += entries.size();
     }
   }
   s_logger.info(
       "Specifications cache = {} tasks for {} specifications", count, _specifications.size());
   s_logger.info("Pending requirements = {}", _pendingRequirements.getValueRequirements().size());
   s_logger.info(
       "Run queue length = {}, deferred queue length = {}",
       _runQueue.size(),
       _deferredQueue.size());
 }
 /**
  * Tests if the graph has been built or if work is still required. Graphs are only built in the
  * background if additional threads is set to non-zero.
  *
  * @return true if the graph has been built, false if it is outstanding
  * @throws CancellationException if the graph build has been canceled
  */
 public boolean isGraphBuilt() {
   // Context is used as the build complete lock
   synchronized (getContext()) {
     synchronized (_activeJobs) {
       if (_cancelled) {
         throw new CancellationException();
       }
       return _activeJobs.isEmpty() && _runQueue.isEmpty() && _deferredQueue.isEmpty();
     }
   }
 }
  /**
   * Add a task to the run queue, increment the count of scheduled steps, and start/wake up a
   * background thread if the run queue was empty, as this indicates that there are probably no
   * active threads at this precise moment.
   *
   * @param runnable the task to add to the run queue
   */
  protected void addToRunQueue(final ContextRunnable runnable) {

    // Check if the run queue is empty
    final boolean dontSpawn = _runQueue.isEmpty();

    // Increment the number of scheduled steps
    _scheduledSteps.incrementAndGet();

    // Actually add the task to this DependencyGraphBuilder's run queue
    _runQueue.add(runnable);

    // Don't start construction jobs if the queue is empty or a sequential piece of work bounces
    // between two threads (i.e. there
    // is already a background thread that is running the caller which can then execute the task it
    // has just put into the run
    // queue). The moment the queue is non-empty, start a job if possible.
    if (!dontSpawn) {
      startBackgroundConstructionJob();
    }
  }