/** * 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(); } }