001package jmri.jmrit.logixng.util;
002
003import java.util.TimerTask;
004
005/**
006 * A timer task that can be stopped, and there the stop method waits until the
007 * task is finished.
008 * <p>
009 * Note that this class does [u]not[/u] work for repeating timers. The class
010 * can be used for tasks that are scheduled over and over again, but only works
011 * for one shoot timer.
012 * <p>
013 * In other words, the class works for TimerUtil.schedule(@Nonnull TimerTask task, long delay)
014 * but not for TimerUtil.schedule(@Nonnull TimerTask task, long delay, long period).
015 * This is due to how the method TimerTask.cancel() works.
016 */
017public abstract class ProtectedTimerTask extends TimerTask {
018
019    private final Object _lock = new Object();
020    private boolean _timerIsRunning = false;
021    private boolean _stopTimer = false;
022    
023    public abstract void execute();
024    
025    @Override
026    public final void run() {
027        synchronized(_lock) {
028            if (_stopTimer) return;
029            _timerIsRunning = true;
030        }
031        
032        // Execute the task
033        execute();
034        
035        synchronized(_lock) {
036            _timerIsRunning = false;
037        }
038    }
039    
040    /**
041     * Stop the timer.
042     * This method will not return until the timer task is cancelled and stopped.
043     * This code ensures that we don't return from this method until the timer
044     * task is cancelled and that it's not running any more.
045     */
046//    @SuppressWarnings(value = "SleepWhileInLoop")
047    public void stopTimer() {
048        synchronized (_lock) {
049            _stopTimer = true;
050            // If cancel() returns true, the task will never be
051            // executed and we are done.
052            if (cancel()) return;
053            // If the timer task is not running, we don't have
054            // to wait for it to finish.
055            if (!_timerIsRunning) {
056                return;
057            }
058        }
059        // Try max 50 times
060        for (int count=0; count <= 50; count++) {
061            synchronized (_lock) {
062                if (!_timerIsRunning) return;
063            }
064            try {
065                Thread.sleep(20);
066            } catch (InterruptedException e) {
067                Thread.currentThread().interrupt();
068            }
069        }
070        throw new RuntimeException("Cannot stop timer");
071    }
072
073}