Mercurial > hg > jslab
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 } |