samer@0: /* samer@0: * Copyright (c) 2000, Samer Abdallah, King's College London. samer@0: * All rights reserved. samer@0: * samer@0: * This software is provided AS iS and WITHOUT ANY WARRANTY; samer@0: * without even the implied warranty of MERCHANTABILITY or samer@0: * FITNESS FOR A PARTICULAR PURPOSE. samer@0: */ samer@0: samer@0: package samer.tools; samer@0: samer@0: import samer.core.*; samer@0: import samer.core.util.*; samer@0: import samer.core.types.*; samer@0: import java.util.*; samer@0: samer@0: /** frame rate regulator */ samer@0: samer@0: public class RThread extends Viewable implements Agent, Runnable samer@0: { samer@0: Task task, termhook; samer@0: Thread thread; samer@0: int prio; samer@0: boolean running; samer@0: int numloops=0; samer@0: samer@0: int N=10, count; samer@0: long t0, elapsed, iterations; samer@0: boolean regulated=true; samer@0: VDouble target; // target frame rate samer@0: double actual; // actual frame rate samer@0: long period, wakeTime,nanos; samer@0: samer@0: private volatile boolean paused=false; samer@0: private volatile boolean kill=false; samer@0: samer@0: samer@0: // .....Constructors................................ samer@0: samer@0: public RThread() { this(new NullTask()); } samer@0: public RThread(Task r) samer@0: { samer@0: super("regulator"); samer@0: samer@0: Shell.push(node); samer@0: target = new VDouble("target",50,0); samer@0: regulated = Shell.getBoolean("regulated",regulated); samer@0: setPriority(Shell.getDouble("thread.priority",-0.25)); samer@0: Shell.pop(); samer@0: samer@0: setN(actual=target.value); samer@0: setPeriod(); samer@0: setAgent(this); samer@0: samer@0: task=r; termhook=null; samer@0: thread=new Thread(this); samer@0: thread.setPriority(prio); samer@0: running=false; samer@0: samer@0: Shell.registerAgent(this); samer@0: Shell.registerViewable(this); samer@0: } samer@0: samer@0: public Task onFinish(Task t) { Task old=termhook; termhook=t; return old; } samer@0: public Viewer getViewer() { return new UI(); } samer@0: samer@0: public void dispose() samer@0: { samer@0: Shell.deregisterAgent(this); samer@0: Shell.deregisterViewable(this); samer@0: target.dispose(); samer@0: super.dispose(); samer@0: } samer@0: samer@0: public Task getTask() { return task; } samer@0: public void setTask(Task t) { task=t; } samer@0: samer@0: public class Kill extends Exception {} samer@0: samer@0: public void interrupt() { thread.interrupt(); } samer@0: public void join() { samer@0: try { thread.join(); } samer@0: catch (InterruptedException ex) {} samer@0: } samer@0: samer@0: public void start() { start(0); } samer@0: public void start(int n) samer@0: { samer@0: paused=false; kill=false; samer@0: numloops=n; samer@0: reset(); samer@0: thread=new Thread(this); samer@0: thread.setPriority(prio); samer@0: thread.start(); samer@0: } samer@0: samer@0: /** Important: this method BLOCKS until the samer@0: * thread co-operatively terminates, either naturally samer@0: * or by not handling the InterruptedException that samer@0: * will be thrown by the checkpoint() method samer@0: * the next time it is called. samer@0: */ samer@0: samer@0: public void kill() samer@0: { samer@0: if (!thread.isAlive()) return; samer@0: samer@0: synchronized (this) { samer@0: kill=true; samer@0: paused=true; samer@0: thread.interrupt(); // this will interrupt sleep, wait etc samer@0: } samer@0: try { thread.join(5000); } samer@0: catch (Exception ex) { samer@0: Shell.trace("RThread.kill failed: "+ex); samer@0: } samer@0: } samer@0: samer@0: samer@0: public void setPriority(double p) samer@0: { samer@0: Shell.trace("thread priority is "+p); samer@0: if (p==0) { prio=Thread.NORM_PRIORITY; } samer@0: else if (p>0) { samer@0: prio=Thread.NORM_PRIORITY+(int)(p*(Thread.MAX_PRIORITY-Thread.NORM_PRIORITY)); samer@0: } else { samer@0: prio=Thread.NORM_PRIORITY-(int)(p*(Thread.MIN_PRIORITY-Thread.NORM_PRIORITY)); samer@0: } samer@0: } samer@0: samer@0: public void pause() { paused=true; } samer@0: public void unpause() { samer@0: if (paused) synchronized (this) { samer@0: paused=false; notifyAll(); samer@0: } samer@0: } samer@0: samer@0: /** This method blocks if the thread has been paused samer@0: * and throws an InterruptedException if the thread samer@0: * has been killed. samer@0: */ samer@0: samer@0: public final void checkpoint() throws Kill samer@0: { samer@0: if (paused) synchronized (this) { samer@0: Shell.trace("pausing"); samer@0: running=false; task.stopping(); samer@0: if (kill) throw new Kill(); samer@0: try { wait(); } catch (Exception ex) {} samer@0: if (kill) throw new Kill(); samer@0: running=true; task.starting(); reset(); samer@0: Shell.trace("unpausing"); samer@0: } samer@0: } samer@0: samer@0: public void run() samer@0: { samer@0: try { samer@0: Shell.trace("Regulator: init"); samer@0: task.starting(); samer@0: running=true; samer@0: samer@0: try { samer@0: if (numloops==0) { samer@0: for (;;) { samer@0: task.run(); samer@0: if (regulated) tick(); samer@0: //if (regulated) synchronized (this) { wait(); } samer@0: if (++count>=N) { tock(); changed(); } samer@0: checkpoint(); samer@0: } samer@0: } else { samer@0: int n=numloops; samer@0: numloops=0; samer@0: for (int i=0;i=N) { tock(); changed(); } samer@0: checkpoint(); samer@0: } samer@0: task.stopping(); samer@0: tock(); changed(); samer@0: } samer@0: } samer@0: catch (Kill ex) { throw ex; } samer@0: catch (Exception ex) { task.stopping(); throw ex; } samer@0: finally { samer@0: running=false; samer@0: Shell.trace("Regulator: term"); samer@0: if (termhook!=null) { samer@0: Shell.trace("running termination hook"); samer@0: try { termhook.run(); } samer@0: catch (Exception ex) { ex.printStackTrace(); } samer@0: } samer@0: } samer@0: } samer@0: catch (Kill ex) { samer@0: System.out.println("Regulator: thread killed"); samer@0: } samer@0: catch (Exception ex) { samer@0: System.out.println("Regulator: "+ex); samer@0: ex.printStackTrace(); samer@0: } samer@0: } samer@0: samer@0: private final void tick() throws Exception { samer@0: long now=System.currentTimeMillis(); samer@0: if (now