view src/samer/tools/RThread.java @ 5:b67a33c44de7

Remove some crap, etc
author samer
date Fri, 05 Apr 2019 21:34:25 +0100
parents bf79fb79ee13
children
line wrap: on
line source
/*
 *	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);
		}

	}
}