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