annotate src/samer/tools/RThread.java @ 8:5e3cbbf173aa tip

Reorganise some more
author samer
date Fri, 05 Apr 2019 22:41:58 +0100
parents bf79fb79ee13
children
rev   line source
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 }