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);
}