comparison src/samer/tools/RThread.java @ 0:bf79fb79ee13

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