package org.rhq.cassandra.schema; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author John Sanda */ class TaskTracker { private volatile int remainingTasks; private volatile boolean schedulingFinished; private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private CountDownLatch allTasksFinished = new CountDownLatch(1); private volatile boolean aborted; private String errorMessage; /** * Increases the count of remaining tasks. */ public void addTask() { try { lock.writeLock().lock(); remainingTasks++; } finally { lock.writeLock().unlock(); } } /** * This method is intended primarily for debugging purposes to log the progress. While other methods in this class * obtain a read or write lock, this method intentionally does not. There is no need to impose the locking overhead * since this method only reads a single variable that is a volatile. * * @return The number of remaining or outstanding tasks to be completed */ public int getRemainingTasks() { return remainingTasks; } /** * Should be called by the producer when it has finished scheduling tasks. Moreover the producer must invoke this * method before it invokes {@link #waitForTasksToFinish()}. Failure to do so will cause the producer to block * indefinitely. */ public void finishedSchedulingTasks() { try { lock.writeLock().lock(); schedulingFinished = true; } finally { lock.writeLock().unlock(); } } /** * Should be invoked by a consumer when it completes a task. */ public void finishedTask() { try { lock.writeLock().lock(); remainingTasks--; if (schedulingFinished && remainingTasks == 0) { allTasksFinished.countDown(); } } finally { lock.writeLock().unlock(); } } /** * Should be invoked by the producer <strong>only</strong> after it has invoked {@link #finishedSchedulingTasks()}. * If this method gets invoked first, the producer will block indefinitely. This method will block until all tasks * have completed. If all tasks have already completed, this method returns immediately. * * @throws InterruptedException If the producer thread is interrupted while waiting * @throws AbortedException If task processing has been abort which is accomplished by calling * {@link #abort(String)} */ public void waitForTasksToFinish() throws InterruptedException, AbortedException { try { lock.readLock().lock(); if (aborted) { throw new AbortedException(errorMessage); } if (remainingTasks == 0) { return; } } finally { lock.readLock().unlock(); } allTasksFinished.await(); try { lock.readLock().lock(); if (aborted) { throw new AbortedException(errorMessage); } } finally { lock.readLock().unlock(); } } /** * Should be invoked by a consumer to abort processing of any future tasks. * * @param msg An error message that will be included in the {@link AbortedException} thrown by * {@link #waitForTasksToFinish()} */ public void abort(String msg) { try { lock.writeLock().lock(); errorMessage = msg; aborted = true; allTasksFinished.countDown(); } finally { lock.writeLock().unlock(); } } }