/** * Copyright (c) 2002-2005, Simone Bordet * All rights reserved. * * This software is distributable under the BSD license. * See the terms of the BSD license in the documentation provided with this software. */ package foxtrot.workers; import foxtrot.AbstractWorkerThread; import foxtrot.Task; /** * Full implementation of {@link foxtrot.WorkerThread} that uses a single worker thread to run * {@link foxtrot.Task}s subclasses. <br /> * Tasks execution is serialized: tasks are enqueued and executed one after the other. * @version $Revision: 1.5 $ */ public class SingleWorkerThread extends AbstractWorkerThread implements Runnable { private static int sequence = 0; static final boolean debug = false; private Thread thread; private Link current; private boolean pending; public void start() { if (isAlive()) return; if (debug) System.out.println("[SingleWorkerThread] Starting"); stop(); thread = new Thread(this, getThreadName()); // Daemon, since the JVM should shut down on Event Dispatch Thread termination thread.setDaemon(true); thread.start(); } /** * Returns the name of the worker thread used by this WorkerThread. */ protected String getThreadName() { return "Foxtrot Single Worker Thread #" + nextSequence(); } static synchronized int nextSequence() { return ++sequence; } /** * Stops abruptly this WorkerThread. * If a Task is executing, its execution will be completed, * but pending tasks will not be executed until a restart() */ protected void stop() { if (thread != null) { if (debug) System.out.println("[SingleWorkerThread] Ending " + thread); thread.interrupt(); } } public boolean isAlive() { if (thread == null) return false; return thread.isAlive() && !isThreadInterrupted(); } public boolean isWorkerThread() { return Thread.currentThread() == thread; } /** * Posts the given Task onto an internal queue. * @see #takeTask */ public void postTask(Task t) { // It is possible that the worker thread is stopped when an applet is destroyed. // Here we restart it in case has been stopped. // Useful also if the WorkerThread has been replaced but not started by the user if (!isAlive()) start(); // Synchronized since the variable current is accessed from two threads. // See takeTask() synchronized (this) { if (hasTasks()) { if (debug) System.out.println("[SingleWorkerThread] Task queue not empty, enqueueing task:" + t); // Append the given task at the end of the queue Link item = current; while (item.next != null) item = item.next; item.next = new Link(t); } else { if (debug) System.out.println("[SingleWorkerThread] Task queue empty, adding task:" + t); // Add the given task and notify waiting current = new Link(t); notifyAll(); } } } /** * Removes and returns the first available {@link foxtrot.Task} from the internal queue. * If no Tasks are available, this method blocks until a Task is posted via * {@link #postTask} */ protected Task takeTask() throws InterruptedException { // Synchronized since the variable current is accessed from two threads. // See postTask() synchronized (this) { while (!hasTasks()) { if (debug) System.out.println("[SingleWorkerThread] Task queue empty, waiting for tasks"); pending = false; wait(); } pending = true; // Taking the current task, removing it from the queue Task t = current.task; current = current.next; return t; } } private boolean hasTasks() { synchronized (this) { return current != null; } } boolean hasPendingTasks() { synchronized (this) { return pending; } } /** * Returns whether the worker thread has been interrupted or not. * @see java.lang.Thread#isInterrupted */ protected boolean isThreadInterrupted() { return thread.isInterrupted(); } /** * The worker thread dequeues one {@link foxtrot.Task} from the internal queue via {@link #takeTask} * and then executes it. */ public void run() { if (debug) System.out.println("[SingleWorkerThread] Started " + thread); while (!isThreadInterrupted()) { try { Task t = takeTask(); if (debug) System.out.println("[SingleWorkerThread] Dequeued Task " + t); run(t); } catch (InterruptedException x) { if (debug) System.out.println("[SingleWorkerThread] Interrupted " + thread); Thread.currentThread().interrupt(); break; } } } /** * Executes the given {@link foxtrot.Task}. * This implementation will just call {@link #runTask}. */ protected void run(Task task) { runTask(task); } private static class Link { private Link next; private final Task task; private Link(Task task) { this.task = task; } } }