package org.simantics.modeling.ui.diagramEditor; import gnu.trove.map.hash.TObjectLongHashMap; import java.util.ArrayDeque; import org.eclipse.swt.widgets.Display; public class DisposingPolicy { public static final boolean DEBUG = false; public static final int MAX_QUEUE_LENGTH = 8; public static final long DISPOSE_TIME = 30000L; // ms public static final long MIN_DELAY = 200L; // ms private volatile int maxQueueLength; private ArrayDeque disposerQueue; private TObjectLongHashMap disposeTime; private Runnable currentlyScheduled = null; private Runnable disposeOne = () -> { if(!disposerQueue.isEmpty()) { Runnable runnable = disposerQueue.removeFirst(); disposeTime.remove(runnable); currentlyScheduled = null; runnable.run(); if(DEBUG) System.out.println("Executed disposer " + runnable); //$NON-NLS-1$ if(!disposerQueue.isEmpty()) scheduleDispose(); } }; public DisposingPolicy() { this(MAX_QUEUE_LENGTH); } public DisposingPolicy(int maxQueueLength) { this.maxQueueLength = maxQueueLength; this.disposerQueue = new ArrayDeque<>(maxQueueLength); this.disposeTime = new TObjectLongHashMap<>(maxQueueLength); } public void setMaxQueueLength(int maxQueueLength) { this.maxQueueLength = maxQueueLength; } private void scheduleDispose() { currentlyScheduled = disposerQueue.peekFirst(); long delay = Math.max( disposeTime.get(currentlyScheduled) - System.currentTimeMillis(), MIN_DELAY); Display.getCurrent().timerExec((int)delay, disposeOne); if(DEBUG) System.out.println("Scheduled disposer " + currentlyScheduled + " in " + delay + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /** * Runs the disposer either after DISPOSE_TIME or when there are * more than {@link #maxQueueLength} disposers active and this is first * of them (by activation order). This method must be called from * UI thread. */ public void addDisposer(Runnable disposer) { if(DEBUG) System.out.println("Added disposer " + disposer); //$NON-NLS-1$ if(disposeTime.contains(disposer)) return; if(disposerQueue.size() >= maxQueueLength) disposeOne.run(); disposerQueue.addLast(disposer); disposeTime.put(disposer, System.currentTimeMillis()+DISPOSE_TIME); if(disposerQueue.size() == 1) scheduleDispose(); } /** * Cancels a disposer added before. This method must be called from * UI thread. */ public void removeDisposer(Runnable disposer) { if(DEBUG) System.out.println("Removed disposer " + disposer); //$NON-NLS-1$ disposerQueue.remove(disposer); disposeTime.remove(disposer); if(disposer == currentlyScheduled) { currentlyScheduled = null; if(!disposerQueue.isEmpty()) scheduleDispose(); } } }