package jetbrains.mps.vcs.suspicious; /*Generated by MPS */ import java.util.concurrent.TimeUnit; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import com.intellij.concurrency.JobScheduler; import java.util.ArrayList; import java.util.List; /** * Package-local as we don't need it outside now. If found useful, could be generalized, parameterized with * Condition and lambda that takes collection of T and made public. * Use {@link jetbrains.mps.vcs.suspicious.BaseTaskQueue#start(long, TimeUnit) } and {@link jetbrains.mps.vcs.suspicious.BaseTaskQueue#stop() } to control queue lifecycle * * @param <T> something one can queue and process in batch. */ /*package*/ abstract class BaseTaskQueue<T> { private final LinkedBlockingQueue<T> myTasks = new LinkedBlockingQueue<T>(); private final Semaphore myProcessingSemaphore = new Semaphore(1); private final ScheduledExecutorService myScheduler; private ScheduledFuture<?> myTimerTask; public BaseTaskQueue() { myScheduler = JobScheduler.getScheduler(); } public void start(long period, TimeUnit unit) { if (myTimerTask != null) { throw new IllegalStateException(); } // Don't want a distinct thread sleeping and waiting for tasks to come, that's why got a timer // note, IDEA's scheduler doesn't support schedyleAtFixedRate(). myTimerTask = myScheduler.scheduleWithFixedDelay(new Runnable() { public void run() { scheduleProcessing(); } }, period, period, unit); } /** * Cancels queue polling and discards tasks left, if any */ public void stop() { if (myTimerTask != null) { myTimerTask.cancel(false); myTimerTask = null; myTasks.clear(); } } /*package*/ void scheduleProcessing() { if (myTasks.isEmpty()) { return; } // we don't care to wait until processing starts. If there's one already scheduled, just let it complete, either it would // pick newly added tasks (if not started yet), or would process tasks at the next timer tick, if already running if (myProcessingSemaphore.tryAcquire()) { myScheduler.execute(new Runnable() { public void run() { process(); myProcessingSemaphore.release(); } }); } } public void addTask(T task) { myTasks.add(task); scheduleProcessing(); } /*package*/ void process() { if (!(isProcessingAllowed())) { return; } ArrayList<T> tasks = new ArrayList(); myTasks.drainTo(tasks); if (tasks.isEmpty()) { return; } processTask(tasks); } protected abstract boolean isProcessingAllowed(); protected abstract void processTask(List<T> tasks); }