Mercurial > hg > jslab
diff src/samer/tools/RThread.java @ 0:bf79fb79ee13
Initial Mercurial check in.
author | samer |
---|---|
date | Tue, 17 Jan 2012 17:50:20 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/samer/tools/RThread.java Tue Jan 17 17:50:20 2012 +0000 @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2000, Samer Abdallah, King's College London. + * All rights reserved. + * + * This software is provided AS iS and WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package samer.tools; + +import samer.core.*; +import samer.core.util.*; +import samer.core.types.*; +import java.util.*; + +/** frame rate regulator */ + +public class RThread extends Viewable implements Agent, Runnable +{ + Task task, termhook; + Thread thread; + int prio; + boolean running; + int numloops=0; + + int N=10, count; + long t0, elapsed, iterations; + boolean regulated=true; + VDouble target; // target frame rate + double actual; // actual frame rate + long period, wakeTime,nanos; + + private volatile boolean paused=false; + private volatile boolean kill=false; + + + // .....Constructors................................ + + public RThread() { this(new NullTask()); } + public RThread(Task r) + { + super("regulator"); + + Shell.push(node); + target = new VDouble("target",50,0); + regulated = Shell.getBoolean("regulated",regulated); + setPriority(Shell.getDouble("thread.priority",-0.25)); + Shell.pop(); + + setN(actual=target.value); + setPeriod(); + setAgent(this); + + task=r; termhook=null; + thread=new Thread(this); + thread.setPriority(prio); + running=false; + + Shell.registerAgent(this); + Shell.registerViewable(this); + } + + public Task onFinish(Task t) { Task old=termhook; termhook=t; return old; } + public Viewer getViewer() { return new UI(); } + + public void dispose() + { + Shell.deregisterAgent(this); + Shell.deregisterViewable(this); + target.dispose(); + super.dispose(); + } + + public Task getTask() { return task; } + public void setTask(Task t) { task=t; } + + public class Kill extends Exception {} + + public void interrupt() { thread.interrupt(); } + public void join() { + try { thread.join(); } + catch (InterruptedException ex) {} + } + + public void start() { start(0); } + public void start(int n) + { + paused=false; kill=false; + numloops=n; + reset(); + thread=new Thread(this); + thread.setPriority(prio); + thread.start(); + } + + /** <b>Important:</b> this method BLOCKS until the + * thread co-operatively terminates, either naturally + * or by not handling the InterruptedException that + * will be thrown by the <code>checkpoint()</code> method + * the next time it is called. + */ + + public void kill() + { + if (!thread.isAlive()) return; + + synchronized (this) { + kill=true; + paused=true; + thread.interrupt(); // this will interrupt sleep, wait etc + } + try { thread.join(5000); } + catch (Exception ex) { + Shell.trace("RThread.kill failed: "+ex); + } + } + + + public void setPriority(double p) + { + Shell.trace("thread priority is "+p); + if (p==0) { prio=Thread.NORM_PRIORITY; } + else if (p>0) { + prio=Thread.NORM_PRIORITY+(int)(p*(Thread.MAX_PRIORITY-Thread.NORM_PRIORITY)); + } else { + prio=Thread.NORM_PRIORITY-(int)(p*(Thread.MIN_PRIORITY-Thread.NORM_PRIORITY)); + } + } + + public void pause() { paused=true; } + public void unpause() { + if (paused) synchronized (this) { + paused=false; notifyAll(); + } + } + + /** This method blocks if the thread has been paused + * and throws an InterruptedException if the thread + * has been killed. + */ + + public final void checkpoint() throws Kill + { + if (paused) synchronized (this) { + Shell.trace("pausing"); + running=false; task.stopping(); + if (kill) throw new Kill(); + try { wait(); } catch (Exception ex) {} + if (kill) throw new Kill(); + running=true; task.starting(); reset(); + Shell.trace("unpausing"); + } + } + + public void run() + { + try { + Shell.trace("Regulator: init"); + task.starting(); + running=true; + + try { + if (numloops==0) { + for (;;) { + task.run(); + if (regulated) tick(); + //if (regulated) synchronized (this) { wait(); } + if (++count>=N) { tock(); changed(); } + checkpoint(); + } + } else { + int n=numloops; + numloops=0; + for (int i=0;i<n;i++) { + task.run(); + if (regulated) tick(); + //if (regulated) synchronized (this) { wait(); } + if (++count>=N) { tock(); changed(); } + checkpoint(); + } + task.stopping(); + tock(); changed(); + } + } + catch (Kill ex) { throw ex; } + catch (Exception ex) { task.stopping(); throw ex; } + finally { + running=false; + Shell.trace("Regulator: term"); + if (termhook!=null) { + Shell.trace("running termination hook"); + try { termhook.run(); } + catch (Exception ex) { ex.printStackTrace(); } + } + } + } + catch (Kill ex) { + System.out.println("Regulator: thread killed"); + } + catch (Exception ex) { + System.out.println("Regulator: "+ex); + ex.printStackTrace(); + } + } + + private final void tick() throws Exception { + long now=System.currentTimeMillis(); + if (now<wakeTime) { + Thread.sleep(wakeTime-now); + wakeTime+=period; + } else wakeTime=now+period; + } + + public double fps() { return actual; } + public double elapsedTime() { return elapsed/1000.0; } + public long iterations() { return iterations; } + public void setRate( double r) { target.value=r; target.changed(); } + public void regulate() { regulated=true; setPeriod(); } + public void deregulate() { regulated=false; } + + private void setPeriod() { + period=(long)(1000.0/target.value); + // double T=(1000.0/target.value); + // period = (long)T; + // nanos = (long)(1000000.0*(T-period)); + // Shell.trace("period = "+period+"ms + "+nanos+"ns"); + } + + public void reset() + { + setPeriod(); + count=0; setN(actual); + t0=System.currentTimeMillis(); + wakeTime=t0+period; + } + + + public void tock() + { + long t = System.currentTimeMillis(); + long dt = t-t0; + + elapsed+=dt; + iterations+=count; + actual = (1000.0*count)/dt; + count = 0; t0 = t; + setN(actual); // try to count for 2 seconds + setPeriod(); + } + + private void setN(double rate) + { + // set the number of iterations to time + // given a certain rate + N=(int)(2*rate); // try to count for 2 seconds + if (N==0) N=1; // at least 1! + } + + public void getCommands(Agent.Registry r) { + r.add("start").add("pause").add("kill").add("step"); + r.add("zero"); // do we want this one here? + } + + public void execute(String c, Environment env) throws Exception + { + if (c.equals("start") || c.equals("resume")) { if (thread.isAlive()) unpause(); else start(); } + else if (c.equals("stop") || c.equals("pause")) pause(); + else if (c.equals("step")) { + if (!running) { + task.starting(); + task.run(); + task.stopping(); + } else { + Shell.print("Cannot step while thread is running."); + } + } else if (c.equals("regulated")) { + regulated = X._bool(env.datum(),!regulated); + reset(); changed(); + } else if (c.equals("zero")) { + tock(); elapsed=iterations=0; changed(); + } else if (c.equals("tock")) { tock(); changed(); } + else if (c.equals("exit") || c.equals("kill")) { + Shell.trace("killing thread..."); + kill(); + Shell.trace("thread killed."); + } + } + + class UI extends BaseViewer + { + NumberViewer actualRate=Shell.createNumberViewer("Actual rate",NumberViewer.TEXT); + NumberViewer elapsedTime=Shell.createNumberViewer("Elapsed time",NumberViewer.TEXT); + NumberViewer iterationCount=Shell.createNumberViewer("Iterations",NumberViewer.TEXT); + Viewer targetViewer; + VBoolean reg; + + UI() + { + super(RThread.this); + setLayout( new StackLayout(2)); + panel().setName(getLabel()); + + targetViewer = target.getViewer(); + reg = new VBoolean("regulated",regulated,0); + reg.addObserver(this); + + add( targetViewer.getComponent()); + add( actualRate.getComponent()); + add( elapsedTime.getComponent()); + add( iterationCount.getComponent()); + add( reg.getViewer().getComponent()); + + panel().add(Shell.createButtonsFor(RThread.this)); + } + + public void attach() { targetViewer.attach(); super.attach(); } + public void detach() { targetViewer.detach(); super.detach(); } + + public void update(Observable o, Object src) + { + if (reg==o && src!=this) { + regulated = reg.value; setPeriod(); + } + if (reg.value!=regulated) { + reg.value=regulated; reg.changed(this); + } + + actualRate.set((int)actual); + elapsedTime.set((double)(elapsed/1000.0)); + iterationCount.set((int)(iterations)); + super.update(o,src); + } + + } +}