/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven; import java.util.*; public class Defer extends ThreadGroup { private static final Map<ThreadGroup, Defer> groups = new WeakHashMap<ThreadGroup, Defer>(); private final Queue<Future<?>> queue = new PrioQueue<Future<?>>(); private final Collection<Thread> pool = new LinkedList<Thread>(); private final int maxthreads = 2; public interface Callable<T> { public T call() throws InterruptedException; } public static class CancelledException extends RuntimeException { public CancelledException() { super("Execution cancelled"); } public CancelledException(Throwable cause) { super(cause); } } public static class DeferredException extends RuntimeException { public DeferredException(Throwable cause) { super(cause); } } public static class NotDoneException extends Loading { public final Future future; public NotDoneException(Future future) { this.future = future; } public boolean canwait() {return(true);} public void waitfor() throws InterruptedException { synchronized(future) { while(!future.done()) future.wait(); } } } public class Future<T> implements Runnable, Prioritized { public final Callable<T> task; private int prio = 0; private T val; private volatile String state = ""; private RuntimeException exc = null; private Thread running = null; private Future(Callable<T> task) { this.task = task; } public void cancel() { synchronized(this) { if(running != null) { running.interrupt(); } else { exc = new CancelledException(); chstate("done"); } } } private void chstate(String nst) { synchronized(this) { this.state = nst; notifyAll(); } } public void run() { synchronized(this) { if(state == "done") return; running = Thread.currentThread(); } try { val = task.call(); chstate("done"); } catch(InterruptedException exc) { this.exc = new CancelledException(exc); chstate("done"); } catch(Loading exc) { } catch(RuntimeException exc) { this.exc = exc; chstate("done"); } finally { if(state != "done") chstate("resched"); running = null; } } public T get() { synchronized(this) { boostprio(5); if(state == "done") { if(exc != null) throw(new DeferredException(exc)); return(val); } if(state == "resched") { defer(this); state = ""; } throw(new NotDoneException(this)); } } public boolean done() { synchronized(this) { boostprio(5); if(state == "resched") { defer(this); state = ""; } return(state == "done"); } } public int priority() { return(prio); } public void boostprio(int prio) { synchronized(this) { if(this.prio < prio) this.prio = prio; } } } private class Worker extends HackThread { private Worker() { super(Defer.this, null, "Worker thread"); setDaemon(true); } public void run() { try { while(true) { Future<?> f; try { long start = System.currentTimeMillis(); synchronized(queue) { while((f = queue.poll()) == null) { if(System.currentTimeMillis() - start > 5000) return; queue.wait(1000); } } } catch(InterruptedException e) { return; } f.run(); f = null; } } finally { synchronized(queue) { pool.remove(this); if((pool.size() < 1) && !queue.isEmpty()) { Thread n = new Worker(); n.start(); pool.add(n); } } } } } public Defer(ThreadGroup parent) { super(parent, "DPC threads"); } private void defer(Future<?> f) { synchronized(queue) { boolean e = queue.isEmpty(); queue.add(f); queue.notify(); if((pool.isEmpty() || !e) && (pool.size() < maxthreads)) { Thread n = new Worker(); n.start(); pool.add(n); } } } public <T> Future<T> defer(Callable<T> task) { Future<T> f = new Future<T>(task); defer(f); return(f); } public static <T> Future<T> later(Callable<T> task) { ThreadGroup tg = Thread.currentThread().getThreadGroup(); if(tg instanceof Defer) return(((Defer)tg).defer(task)); Defer d; synchronized(groups) { if((d = groups.get(tg)) == null) groups.put(tg, d = new Defer(tg)); } return(d.defer(task)); } }