RerunnableThread.java

package eu.javaexperience.multithread;


import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import eu.javaexperience.interfaces.simple.publish.SimplePublish2;

public abstract class RerunnableThread<T>
{
	protected final Semaphore accept = new Semaphore(0);
	protected final Semaphore free = new Semaphore(0);
	protected volatile T param = null;

	protected volatile long lastUsed = 0;
	
	private final Thread worker = new Thread()
	{
		@Override
		public void run()
		{
			while(true)
			{
				try
				{
					free.release();
					accept.acquire();
				}
				catch (InterruptedException e)
				{
					return;
				}
				
				try
				{
					runThis(param);
				}
				catch(Throwable e)
				{
					if(e == POISON)
						return;
					
					System.err.println("TOPLEVEL UNCATCHED EXCEPTION");
					e.printStackTrace();
					
					try
					{
						if(onException!=null)
							onException.publish(RerunnableThread.this,e);
					}
					catch(Exception sad)
					{}
				}
				
				param = null;
			}
		}
	};

	public RerunnableThread(boolean daemon)
	{
		worker.setDaemon(daemon);
		worker.start();
	}
	
	public RerunnableThread()
	{
		this(false);
	}
	
	public StackTraceElement[] getStackTraceElements()
	{
		return worker.getStackTrace();
	}
	
	public static final Error POISON = MultithreadingTools.THREAD_SHUTDOWN_POISON;
	
	protected void stopCallerThread()
	{
		throw POISON;
	}
	
	public boolean isDaemon()
	{
		return worker.isDaemon();
	}
	
	public void waitFree(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
	{
		if(free.tryAcquire(timeout, unit))
		{
			free.release();
			return;
		}
		throw new TimeoutException();
	}
	
	public long getLastUsed()
	{
		return lastUsed;
	}
	
	public boolean tryRerun(T param)
	{
		if(free.tryAcquire())
		{
			this.param = param;
			lastUsed = System.currentTimeMillis();
			accept.release();
			return true;
		}
		return false;
	}
	
	public boolean isFree()
	{
		return 0 != free.availablePermits();
	}

	public T getParam()
	{
		return param;
	}

	public abstract void runThis(T param) throws Throwable;
	
	protected SimplePublish2<RerunnableThread<T>,Throwable> onException = null;
	
	@Override
	public void finalize()
	{
		worker.interrupt();
	}
}