samer@0
|
1 /*
|
samer@0
|
2 * Copyright (c) 2000, Samer Abdallah, King's College London.
|
samer@0
|
3 * All rights reserved.
|
samer@0
|
4 *
|
samer@0
|
5 * This software is provided AS iS and WITHOUT ANY WARRANTY;
|
samer@0
|
6 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
7 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
8 */
|
samer@0
|
9
|
samer@0
|
10 package samer.tools;
|
samer@0
|
11
|
samer@0
|
12 import samer.core.*;
|
samer@0
|
13 import samer.core.util.*;
|
samer@0
|
14 import samer.core.types.*;
|
samer@0
|
15 import java.util.*;
|
samer@0
|
16
|
samer@0
|
17 /** frame rate regulator */
|
samer@0
|
18
|
samer@0
|
19 public class RThread extends Viewable implements Agent, Runnable
|
samer@0
|
20 {
|
samer@0
|
21 Task task, termhook;
|
samer@0
|
22 Thread thread;
|
samer@0
|
23 int prio;
|
samer@0
|
24 boolean running;
|
samer@0
|
25 int numloops=0;
|
samer@0
|
26
|
samer@0
|
27 int N=10, count;
|
samer@0
|
28 long t0, elapsed, iterations;
|
samer@0
|
29 boolean regulated=true;
|
samer@0
|
30 VDouble target; // target frame rate
|
samer@0
|
31 double actual; // actual frame rate
|
samer@0
|
32 long period, wakeTime,nanos;
|
samer@0
|
33
|
samer@0
|
34 private volatile boolean paused=false;
|
samer@0
|
35 private volatile boolean kill=false;
|
samer@0
|
36
|
samer@0
|
37
|
samer@0
|
38 // .....Constructors................................
|
samer@0
|
39
|
samer@0
|
40 public RThread() { this(new NullTask()); }
|
samer@0
|
41 public RThread(Task r)
|
samer@0
|
42 {
|
samer@0
|
43 super("regulator");
|
samer@0
|
44
|
samer@0
|
45 Shell.push(node);
|
samer@0
|
46 target = new VDouble("target",50,0);
|
samer@0
|
47 regulated = Shell.getBoolean("regulated",regulated);
|
samer@0
|
48 setPriority(Shell.getDouble("thread.priority",-0.25));
|
samer@0
|
49 Shell.pop();
|
samer@0
|
50
|
samer@0
|
51 setN(actual=target.value);
|
samer@0
|
52 setPeriod();
|
samer@0
|
53 setAgent(this);
|
samer@0
|
54
|
samer@0
|
55 task=r; termhook=null;
|
samer@0
|
56 thread=new Thread(this);
|
samer@0
|
57 thread.setPriority(prio);
|
samer@0
|
58 running=false;
|
samer@0
|
59
|
samer@0
|
60 Shell.registerAgent(this);
|
samer@0
|
61 Shell.registerViewable(this);
|
samer@0
|
62 }
|
samer@0
|
63
|
samer@0
|
64 public Task onFinish(Task t) { Task old=termhook; termhook=t; return old; }
|
samer@0
|
65 public Viewer getViewer() { return new UI(); }
|
samer@0
|
66
|
samer@0
|
67 public void dispose()
|
samer@0
|
68 {
|
samer@0
|
69 Shell.deregisterAgent(this);
|
samer@0
|
70 Shell.deregisterViewable(this);
|
samer@0
|
71 target.dispose();
|
samer@0
|
72 super.dispose();
|
samer@0
|
73 }
|
samer@0
|
74
|
samer@0
|
75 public Task getTask() { return task; }
|
samer@0
|
76 public void setTask(Task t) { task=t; }
|
samer@0
|
77
|
samer@0
|
78 public class Kill extends Exception {}
|
samer@0
|
79
|
samer@0
|
80 public void interrupt() { thread.interrupt(); }
|
samer@0
|
81 public void join() {
|
samer@0
|
82 try { thread.join(); }
|
samer@0
|
83 catch (InterruptedException ex) {}
|
samer@0
|
84 }
|
samer@0
|
85
|
samer@0
|
86 public void start() { start(0); }
|
samer@0
|
87 public void start(int n)
|
samer@0
|
88 {
|
samer@0
|
89 paused=false; kill=false;
|
samer@0
|
90 numloops=n;
|
samer@0
|
91 reset();
|
samer@0
|
92 thread=new Thread(this);
|
samer@0
|
93 thread.setPriority(prio);
|
samer@0
|
94 thread.start();
|
samer@0
|
95 }
|
samer@0
|
96
|
samer@0
|
97 /** <b>Important:</b> this method BLOCKS until the
|
samer@0
|
98 * thread co-operatively terminates, either naturally
|
samer@0
|
99 * or by not handling the InterruptedException that
|
samer@0
|
100 * will be thrown by the <code>checkpoint()</code> method
|
samer@0
|
101 * the next time it is called.
|
samer@0
|
102 */
|
samer@0
|
103
|
samer@0
|
104 public void kill()
|
samer@0
|
105 {
|
samer@0
|
106 if (!thread.isAlive()) return;
|
samer@0
|
107
|
samer@0
|
108 synchronized (this) {
|
samer@0
|
109 kill=true;
|
samer@0
|
110 paused=true;
|
samer@0
|
111 thread.interrupt(); // this will interrupt sleep, wait etc
|
samer@0
|
112 }
|
samer@0
|
113 try { thread.join(5000); }
|
samer@0
|
114 catch (Exception ex) {
|
samer@0
|
115 Shell.trace("RThread.kill failed: "+ex);
|
samer@0
|
116 }
|
samer@0
|
117 }
|
samer@0
|
118
|
samer@0
|
119
|
samer@0
|
120 public void setPriority(double p)
|
samer@0
|
121 {
|
samer@0
|
122 Shell.trace("thread priority is "+p);
|
samer@0
|
123 if (p==0) { prio=Thread.NORM_PRIORITY; }
|
samer@0
|
124 else if (p>0) {
|
samer@0
|
125 prio=Thread.NORM_PRIORITY+(int)(p*(Thread.MAX_PRIORITY-Thread.NORM_PRIORITY));
|
samer@0
|
126 } else {
|
samer@0
|
127 prio=Thread.NORM_PRIORITY-(int)(p*(Thread.MIN_PRIORITY-Thread.NORM_PRIORITY));
|
samer@0
|
128 }
|
samer@0
|
129 }
|
samer@0
|
130
|
samer@0
|
131 public void pause() { paused=true; }
|
samer@0
|
132 public void unpause() {
|
samer@0
|
133 if (paused) synchronized (this) {
|
samer@0
|
134 paused=false; notifyAll();
|
samer@0
|
135 }
|
samer@0
|
136 }
|
samer@0
|
137
|
samer@0
|
138 /** This method blocks if the thread has been paused
|
samer@0
|
139 * and throws an InterruptedException if the thread
|
samer@0
|
140 * has been killed.
|
samer@0
|
141 */
|
samer@0
|
142
|
samer@0
|
143 public final void checkpoint() throws Kill
|
samer@0
|
144 {
|
samer@0
|
145 if (paused) synchronized (this) {
|
samer@0
|
146 Shell.trace("pausing");
|
samer@0
|
147 running=false; task.stopping();
|
samer@0
|
148 if (kill) throw new Kill();
|
samer@0
|
149 try { wait(); } catch (Exception ex) {}
|
samer@0
|
150 if (kill) throw new Kill();
|
samer@0
|
151 running=true; task.starting(); reset();
|
samer@0
|
152 Shell.trace("unpausing");
|
samer@0
|
153 }
|
samer@0
|
154 }
|
samer@0
|
155
|
samer@0
|
156 public void run()
|
samer@0
|
157 {
|
samer@0
|
158 try {
|
samer@0
|
159 Shell.trace("Regulator: init");
|
samer@0
|
160 task.starting();
|
samer@0
|
161 running=true;
|
samer@0
|
162
|
samer@0
|
163 try {
|
samer@0
|
164 if (numloops==0) {
|
samer@0
|
165 for (;;) {
|
samer@0
|
166 task.run();
|
samer@0
|
167 if (regulated) tick();
|
samer@0
|
168 //if (regulated) synchronized (this) { wait(); }
|
samer@0
|
169 if (++count>=N) { tock(); changed(); }
|
samer@0
|
170 checkpoint();
|
samer@0
|
171 }
|
samer@0
|
172 } else {
|
samer@0
|
173 int n=numloops;
|
samer@0
|
174 numloops=0;
|
samer@0
|
175 for (int i=0;i<n;i++) {
|
samer@0
|
176 task.run();
|
samer@0
|
177 if (regulated) tick();
|
samer@0
|
178 //if (regulated) synchronized (this) { wait(); }
|
samer@0
|
179 if (++count>=N) { tock(); changed(); }
|
samer@0
|
180 checkpoint();
|
samer@0
|
181 }
|
samer@0
|
182 task.stopping();
|
samer@0
|
183 tock(); changed();
|
samer@0
|
184 }
|
samer@0
|
185 }
|
samer@0
|
186 catch (Kill ex) { throw ex; }
|
samer@0
|
187 catch (Exception ex) { task.stopping(); throw ex; }
|
samer@0
|
188 finally {
|
samer@0
|
189 running=false;
|
samer@0
|
190 Shell.trace("Regulator: term");
|
samer@0
|
191 if (termhook!=null) {
|
samer@0
|
192 Shell.trace("running termination hook");
|
samer@0
|
193 try { termhook.run(); }
|
samer@0
|
194 catch (Exception ex) { ex.printStackTrace(); }
|
samer@0
|
195 }
|
samer@0
|
196 }
|
samer@0
|
197 }
|
samer@0
|
198 catch (Kill ex) {
|
samer@0
|
199 System.out.println("Regulator: thread killed");
|
samer@0
|
200 }
|
samer@0
|
201 catch (Exception ex) {
|
samer@0
|
202 System.out.println("Regulator: "+ex);
|
samer@0
|
203 ex.printStackTrace();
|
samer@0
|
204 }
|
samer@0
|
205 }
|
samer@0
|
206
|
samer@0
|
207 private final void tick() throws Exception {
|
samer@0
|
208 long now=System.currentTimeMillis();
|
samer@0
|
209 if (now<wakeTime) {
|
samer@0
|
210 Thread.sleep(wakeTime-now);
|
samer@0
|
211 wakeTime+=period;
|
samer@0
|
212 } else wakeTime=now+period;
|
samer@0
|
213 }
|
samer@0
|
214
|
samer@0
|
215 public double fps() { return actual; }
|
samer@0
|
216 public double elapsedTime() { return elapsed/1000.0; }
|
samer@0
|
217 public long iterations() { return iterations; }
|
samer@0
|
218 public void setRate( double r) { target.value=r; target.changed(); }
|
samer@0
|
219 public void regulate() { regulated=true; setPeriod(); }
|
samer@0
|
220 public void deregulate() { regulated=false; }
|
samer@0
|
221
|
samer@0
|
222 private void setPeriod() {
|
samer@0
|
223 period=(long)(1000.0/target.value);
|
samer@0
|
224 // double T=(1000.0/target.value);
|
samer@0
|
225 // period = (long)T;
|
samer@0
|
226 // nanos = (long)(1000000.0*(T-period));
|
samer@0
|
227 // Shell.trace("period = "+period+"ms + "+nanos+"ns");
|
samer@0
|
228 }
|
samer@0
|
229
|
samer@0
|
230 public void reset()
|
samer@0
|
231 {
|
samer@0
|
232 setPeriod();
|
samer@0
|
233 count=0; setN(actual);
|
samer@0
|
234 t0=System.currentTimeMillis();
|
samer@0
|
235 wakeTime=t0+period;
|
samer@0
|
236 }
|
samer@0
|
237
|
samer@0
|
238
|
samer@0
|
239 public void tock()
|
samer@0
|
240 {
|
samer@0
|
241 long t = System.currentTimeMillis();
|
samer@0
|
242 long dt = t-t0;
|
samer@0
|
243
|
samer@0
|
244 elapsed+=dt;
|
samer@0
|
245 iterations+=count;
|
samer@0
|
246 actual = (1000.0*count)/dt;
|
samer@0
|
247 count = 0; t0 = t;
|
samer@0
|
248 setN(actual); // try to count for 2 seconds
|
samer@0
|
249 setPeriod();
|
samer@0
|
250 }
|
samer@0
|
251
|
samer@0
|
252 private void setN(double rate)
|
samer@0
|
253 {
|
samer@0
|
254 // set the number of iterations to time
|
samer@0
|
255 // given a certain rate
|
samer@0
|
256 N=(int)(2*rate); // try to count for 2 seconds
|
samer@0
|
257 if (N==0) N=1; // at least 1!
|
samer@0
|
258 }
|
samer@0
|
259
|
samer@0
|
260 public void getCommands(Agent.Registry r) {
|
samer@0
|
261 r.add("start").add("pause").add("kill").add("step");
|
samer@0
|
262 r.add("zero"); // do we want this one here?
|
samer@0
|
263 }
|
samer@0
|
264
|
samer@0
|
265 public void execute(String c, Environment env) throws Exception
|
samer@0
|
266 {
|
samer@0
|
267 if (c.equals("start") || c.equals("resume")) { if (thread.isAlive()) unpause(); else start(); }
|
samer@0
|
268 else if (c.equals("stop") || c.equals("pause")) pause();
|
samer@0
|
269 else if (c.equals("step")) {
|
samer@0
|
270 if (!running) {
|
samer@0
|
271 task.starting();
|
samer@0
|
272 task.run();
|
samer@0
|
273 task.stopping();
|
samer@0
|
274 } else {
|
samer@0
|
275 Shell.print("Cannot step while thread is running.");
|
samer@0
|
276 }
|
samer@0
|
277 } else if (c.equals("regulated")) {
|
samer@0
|
278 regulated = X._bool(env.datum(),!regulated);
|
samer@0
|
279 reset(); changed();
|
samer@0
|
280 } else if (c.equals("zero")) {
|
samer@0
|
281 tock(); elapsed=iterations=0; changed();
|
samer@0
|
282 } else if (c.equals("tock")) { tock(); changed(); }
|
samer@0
|
283 else if (c.equals("exit") || c.equals("kill")) {
|
samer@0
|
284 Shell.trace("killing thread...");
|
samer@0
|
285 kill();
|
samer@0
|
286 Shell.trace("thread killed.");
|
samer@0
|
287 }
|
samer@0
|
288 }
|
samer@0
|
289
|
samer@0
|
290 class UI extends BaseViewer
|
samer@0
|
291 {
|
samer@0
|
292 NumberViewer actualRate=Shell.createNumberViewer("Actual rate",NumberViewer.TEXT);
|
samer@0
|
293 NumberViewer elapsedTime=Shell.createNumberViewer("Elapsed time",NumberViewer.TEXT);
|
samer@0
|
294 NumberViewer iterationCount=Shell.createNumberViewer("Iterations",NumberViewer.TEXT);
|
samer@0
|
295 Viewer targetViewer;
|
samer@0
|
296 VBoolean reg;
|
samer@0
|
297
|
samer@0
|
298 UI()
|
samer@0
|
299 {
|
samer@0
|
300 super(RThread.this);
|
samer@0
|
301 setLayout( new StackLayout(2));
|
samer@0
|
302 panel().setName(getLabel());
|
samer@0
|
303
|
samer@0
|
304 targetViewer = target.getViewer();
|
samer@0
|
305 reg = new VBoolean("regulated",regulated,0);
|
samer@0
|
306 reg.addObserver(this);
|
samer@0
|
307
|
samer@0
|
308 add( targetViewer.getComponent());
|
samer@0
|
309 add( actualRate.getComponent());
|
samer@0
|
310 add( elapsedTime.getComponent());
|
samer@0
|
311 add( iterationCount.getComponent());
|
samer@0
|
312 add( reg.getViewer().getComponent());
|
samer@0
|
313
|
samer@0
|
314 panel().add(Shell.createButtonsFor(RThread.this));
|
samer@0
|
315 }
|
samer@0
|
316
|
samer@0
|
317 public void attach() { targetViewer.attach(); super.attach(); }
|
samer@0
|
318 public void detach() { targetViewer.detach(); super.detach(); }
|
samer@0
|
319
|
samer@0
|
320 public void update(Observable o, Object src)
|
samer@0
|
321 {
|
samer@0
|
322 if (reg==o && src!=this) {
|
samer@0
|
323 regulated = reg.value; setPeriod();
|
samer@0
|
324 }
|
samer@0
|
325 if (reg.value!=regulated) {
|
samer@0
|
326 reg.value=regulated; reg.changed(this);
|
samer@0
|
327 }
|
samer@0
|
328
|
samer@0
|
329 actualRate.set((int)actual);
|
samer@0
|
330 elapsedTime.set((double)(elapsed/1000.0));
|
samer@0
|
331 iterationCount.set((int)(iterations));
|
samer@0
|
332 super.update(o,src);
|
samer@0
|
333 }
|
samer@0
|
334
|
samer@0
|
335 }
|
samer@0
|
336 }
|