001: /*=============================================================================
002: * Copyright Texas Instruments 2002. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package ti.pub;
020:
021: import java.util.LinkedList;
022: import java.util.Timer;
023: import java.util.TimerTask;
024:
025: /**
026: * A worker thread is a thread that runs queued runnables, in a FIFO
027: * manner.
028: *
029: * @author ;Rob Clark;a0873619;San Diego;;
030: * @version 0.1
031: */
032: public class WorkerThread extends Thread {
033: /**
034: * The queue of runnables that need invoking
035: */
036: private LinkedList workQueue = new LinkedList();
037:
038: /**
039: * The watchdog timeout, or <code>0</code> for no watchdog
040: */
041: private long watchdogTimeout;
042:
043: /**
044: * Class Constructor. Construct a worker thread with the specified
045: * priority.
046: *
047: * @param pri the priority
048: */
049: public WorkerThread(int pri) {
050: this (pri, 0);
051: }
052:
053: /**
054: * Class Constructor. Construct a worker thread with the specified
055: * priority.
056: *
057: * @param pri the priority
058: * @param watchdogTimeout the watchdog timeout, if a runnable takes
059: * longer than this many ms to complete, the {@link #watchdogTimeoutExceeded}
060: * method will be called from some other thread context. A value of
061: * <code>0</code> indicates no time limit
062: */
063: public WorkerThread(int pri, long watchdogTimeout) {
064: this ("worker", pri, watchdogTimeout);
065: }
066:
067: /**
068: * Class Constructor. Construct a worker thread with the specified
069: * priority.
070: *
071: * @param name the thread name
072: * @param pri the priority
073: * @param watchdogTimeout the watchdog timeout, if a runnable takes
074: * longer than this many ms to complete, the {@link #watchdogTimeoutExceeded}
075: * method will be called from some other thread context. A value of
076: * <code>0</code> indicates no time limit
077: */
078: public WorkerThread(String name, int pri, long watchdogTimeout) {
079: super (name);
080:
081: setPriority(pri);
082:
083: this .watchdogTimeout = watchdogTimeout;
084:
085: // if using watchdog, ensure watchdog timer is started:
086: if (watchdogTimeout != 0) {
087: synchronized (WorkerThread.class) {
088: if (watchdogTimer == null)
089: watchdogTimer = new Timer();
090: }
091: }
092:
093: start();
094: }
095:
096: /**
097: * thread-main
098: */
099: public void run() {
100: while (true) {
101: try {
102: runNext(0);
103: } catch (Throwable t) {
104: unhandledException(t);
105: }
106: }
107: }
108:
109: public void run(Runnable r) {
110: r.run();
111: }
112:
113: /**
114: * Called when unhandled errors occur. Can be overriden.
115: */
116: public void unhandledException(Throwable e) {
117: throw new ti.exceptions.ProgrammingErrorException(e);
118: }
119:
120: /**
121: * If using the watchdog timer, this is called from some other thread context
122: * when the timeout set for executing a runnable has been exceeded.
123: */
124: public void watchdogTimeoutExceeded() {
125: interrupt();
126: }
127:
128: /**
129: * Run the next work unit (runnable). This is normally called repeatedly in
130: * a loop by the worker thread, but can be called externally in places where
131: * the worker thread may get blocked (ie. showing a modal dialog, etc.).
132: * <p>
133: * This should only be called from the context of this worker thread.
134: *
135: * @param timeout the maximum number of ms to wait for the next work item.
136: * A value of <code>0</code> indicates to wait forever
137: */
138: public synchronized void runNext(long timeout)
139: throws InterruptedException {
140: // if there is a current watchdog, cancel it, and store the time remaining
141: // so we can restart it when we are done:
142: long watchdogTimeRemaining = cancelWatchdogTimer();
143:
144: try {
145: Runnable r = null;
146:
147: synchronized (workQueue) {
148: while (workQueue.size() == 0) {
149: long t = System.currentTimeMillis();
150: workQueue.wait(timeout);
151: timeout -= System.currentTimeMillis() - t;
152:
153: if (timeout <= 0)
154: return;
155: }
156:
157: if (workQueue.size() > 0)
158: r = (Runnable) (workQueue.removeFirst());
159: }
160:
161: if (r != null) {
162: try {
163: startWatchdogTimer(watchdogTimeout);
164: run(r);
165: } finally {
166: cancelWatchdogTimer();
167: }
168: }
169: } finally {
170: // if needed, restart previous watchdog:
171: startWatchdogTimer(watchdogTimeRemaining);
172: }
173: }
174:
175: /**
176: * Enqueue a work unit (a runnable) to be invoked from the context of the
177: * worker thread
178: *
179: * @param r the runnable
180: */
181: public void invokeLater(Runnable r) {
182: synchronized (workQueue) {
183: workQueue.addLast(r);
184: workQueue.notify();
185: }
186: }
187:
188: /****/
189:
190: /**
191: * The timer created for watching for expired watchdog timers. All worker
192: * threads using a watchdog share a single timer, but schedule their own
193: * timer-tasks
194: */
195: private static Timer watchdogTimer = null;
196:
197: /**
198: * If using watchdog timer, and there is currently a runnable being executated
199: * this will be the timer task that is scheduled to detect timeout
200: */
201: private WatchdogTimerTask watchdogTimerTask = null;
202:
203: private void startWatchdogTimer(long duration) {
204: if (watchdogTimerTask != null)
205: throw new ti.exceptions.ProgrammingErrorException(
206: "timer already running");
207: if (duration != 0)
208: watchdogTimerTask = new WatchdogTimerTask(duration);
209: }
210:
211: private long cancelWatchdogTimer() {
212: if (watchdogTimerTask != null) {
213: watchdogTimerTask.cancel();
214: long watchdogTimeRemaining = watchdogTimerTask
215: .remainingTime();
216: watchdogTimerTask = null;
217: return watchdogTimeRemaining;
218: } else {
219: return 0;
220: }
221: }
222:
223: /**
224: * Class for implementing watchdog timers. If there is a watchdog, this timer
225: * task will schedule itself
226: */
227: private class WatchdogTimerTask extends TimerTask {
228: private long t;
229: private long startTime = System.currentTimeMillis();
230: private boolean hasRun = false;
231:
232: WatchdogTimerTask(long t) {
233: this .t = t;
234: if (watchdogTimeout != 0)
235: watchdogTimer.schedule(this , t);
236: }
237:
238: public synchronized long remainingTime() {
239: long remaining = t
240: - (System.currentTimeMillis() - startTime);
241: if (remaining <= 0) {
242: run();
243: return 0;
244: }
245: return remaining;
246: }
247:
248: public synchronized void run() {
249: if (!hasRun) {
250: hasRun = true;
251: watchdogTimeoutExceeded();
252: }
253: }
254: }
255: }
256:
257: /*
258: * Local Variables:
259: * tab-width: 2
260: * indent-tabs-mode: nil
261: * mode: java
262: * c-indentation-style: java
263: * c-basic-offset: 2
264: * eval: (c-set-offset 'substatement-open '0)
265: * eval: (c-set-offset 'case-label '+)
266: * eval: (c-set-offset 'inclass '+)
267: * eval: (c-set-offset 'inline-open '0)
268: * End:
269: */
|