001package jmri.util;
002
003import java.util.Date;
004import java.util.Timer;
005import java.util.TimerTask;
006import javax.annotation.Nonnull;
007
008
009/**
010 * Common utility methods for working with (@link java.util.Timer)
011 * <p>
012 * Each {@link java.util.Timer} uses a thread, which means that they're
013 * not throw-away timers:  You either track when you can destroy them
014 * (and that destruction is not obvious), or they stick around consuming
015 * resources.
016 * <p>
017 * This class provides most of the functionality of a Timer.
018 * Some differences:
019 * <ul>
020 * <li>When migrating code that uses Timer.cancel() to end operation, you have to
021 *     retain references to the individual TimerTask objects and cancel them instead.
022 * </ul>
023 * <p>
024 * For convenience, this also provides methods to ensure that the task is invoked
025 * on a specific JMRI thread.
026 * <p>
027 * Please note the comment in the {@link Timer} Javadoc about how
028 * {@link java.util.concurrent.ScheduledThreadPoolExecutor} might provide a better
029 * underlying implementation.
030 *
031 * @author Bob Jacobsen Copyright 2018
032 */
033@javax.annotation.concurrent.Immutable
034final public class TimerUtil {
035
036    // Timer implementation methods
037
038    static public void schedule(@Nonnull TimerTask task, @Nonnull Date time) {
039        synchronized (commonTimer) {
040            try {
041                commonTimer.schedule(task, time);
042            } catch (IllegalStateException e) {
043                log.warn("During schedule()", e);
044            }
045        }
046    }
047
048    static public void schedule(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
049        synchronized (commonTimer) {
050            try {
051                commonTimer.schedule(task, firstTime, period);
052            } catch (IllegalStateException e) {
053                log.warn("During schedule()", e);
054            }
055        }
056    }
057
058    static public void schedule(@Nonnull TimerTask task, long delay) {
059        synchronized (commonTimer) {
060            try {
061                commonTimer.schedule(task, delay);
062            } catch (IllegalStateException e) {
063                log.warn("During schedule()", e);
064            }
065        }
066    }
067
068    static public void schedule(@Nonnull TimerTask task, long delay, long period) {
069        synchronized (commonTimer) {
070            try {
071                commonTimer.schedule(task, delay, period);
072            } catch (IllegalStateException e) {
073                log.warn("During schedule()", e);
074            }
075        }
076    }
077
078    static public void scheduleAtFixedRate(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
079        synchronized (commonTimer) {
080            try {
081                commonTimer.schedule(task, firstTime, period);
082            } catch (IllegalStateException e) {
083                log.warn("During schedule()", e);
084            }
085        }
086    }
087
088    static public void scheduleAtFixedRate(@Nonnull TimerTask task, long delay, long period) {
089        synchronized (commonTimer) {
090            try {
091                commonTimer.schedule(task, delay, period);
092            } catch (IllegalStateException e) {
093                log.warn("During schedule()", e);
094            }
095        }
096    }
097
098
099    // GUI-thread implementation methods
100
101    // arrange to run on GUI thread
102    static private TimerTask gtask(TimerTask task) {
103        return new TimerTask(){
104                @Override
105                public void run() {
106                    ThreadingUtil.runOnGUIEventually(() -> {task.run();});
107                }
108        };
109    }
110
111    static public void scheduleOnGUIThread(@Nonnull TimerTask task, @Nonnull Date time) {
112        synchronized (commonTimer) {
113            try {
114                commonTimer.schedule(gtask(task), time);
115            } catch (IllegalStateException e) {
116                log.warn("During schedule()", e);
117            }
118        }
119    }
120
121    static public void scheduleOnGUIThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
122        synchronized (commonTimer) {
123            try {
124                commonTimer.schedule(gtask(task), firstTime, period);
125            } catch (IllegalStateException e) {
126                log.warn("During schedule()", e);
127            }
128        }
129    }
130
131    static public void scheduleOnGUIThread(@Nonnull TimerTask task, long delay) {
132        synchronized (commonTimer) {
133            try {
134                commonTimer.schedule(gtask(task), delay);
135            } catch (IllegalStateException e) {
136                log.warn("During schedule()", e);
137            }
138        }
139    }
140
141    static public void scheduleOnGUIThread(@Nonnull TimerTask task, long delay, long period) {
142        synchronized (commonTimer) {
143            try {
144                commonTimer.schedule(gtask(task), delay, period);
145            } catch (IllegalStateException e) {
146                log.warn("During schedule()", e);
147            }
148        }
149    }
150
151    static public void scheduleAtFixedRateOnGUIThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
152        synchronized (commonTimer) {
153            try {
154                commonTimer.schedule(gtask(task), firstTime, period);
155            } catch (IllegalStateException e) {
156                log.warn("During schedule()", e);
157            }
158        }
159    }
160
161    static public void scheduleAtFixedRateOnGUIThread(@Nonnull TimerTask task, long delay, long period) {
162        synchronized (commonTimer) {
163            try {
164                commonTimer.schedule(gtask(task), delay, period);
165            } catch (IllegalStateException e) {
166                log.warn("During schedule()", e);
167            }
168        }
169    }
170
171
172    // arrange to run on layout thread
173    static private TimerTask ltask(TimerTask task) {
174        return new TimerTask(){
175                @Override
176                public void run() {
177                    ThreadingUtil.runOnLayoutEventually(() -> {task.run();});
178                }
179        };
180    }
181
182    static public void scheduleOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date time) {
183        synchronized (commonTimer) {
184            try {
185                commonTimer.schedule(ltask(task), time);
186            } catch (IllegalStateException e) {
187                log.warn("During schedule()", e);
188            }
189        }
190    }
191
192    static public void scheduleOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
193        synchronized (commonTimer) {
194            try {
195                commonTimer.schedule(ltask(task), firstTime, period);
196            } catch (IllegalStateException e) {
197                log.warn("During schedule()", e);
198            }
199        }
200    }
201
202    static public void scheduleOnLayoutThread(@Nonnull TimerTask task, long delay) {
203        synchronized (commonTimer) {
204            try {
205                commonTimer.schedule(ltask(task), delay);
206            } catch (IllegalStateException e) {
207                log.warn("During schedule()", e);
208            }
209        }
210    }
211
212    static public void scheduleOnLayoutThread(@Nonnull TimerTask task, long delay, long period) {
213        synchronized (commonTimer) {
214            try {
215                commonTimer.schedule(ltask(task), delay, period);
216            } catch (IllegalStateException e) {
217                log.warn("During schedule()", e);
218            }
219        }
220    }
221
222    static public void scheduleAtFixedRateOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
223        synchronized (commonTimer) {
224            try {
225                commonTimer.schedule(ltask(task), firstTime, period);
226            } catch (IllegalStateException e) {
227                log.warn("During schedule()", e);
228            }
229        }
230    }
231
232    static public void scheduleAtFixedRateOnLayoutThread(@Nonnull TimerTask task, long delay, long period) {
233        synchronized (commonTimer) {
234            try {
235                commonTimer.schedule(ltask(task), delay, period);
236            } catch (IllegalStateException e) {
237                log.warn("During schedule()", e);
238            }
239        }
240    }
241
242
243    final static Timer commonTimer = new Timer("JMRI Common Timer", true);
244
245    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TimerUtil.class);
246}