diff --git a/src/dr/app/beast/BeastMain.java b/src/dr/app/beast/BeastMain.java index 3bec257252..5b6e02fbed 100644 --- a/src/dr/app/beast/BeastMain.java +++ b/src/dr/app/beast/BeastMain.java @@ -49,7 +49,6 @@ import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.logging.*; @@ -80,11 +79,8 @@ public BeastConsoleApp(String nameString, String titleString, String aboutString } public void doStop() { - Iterator iter = parser.getThreads(); - while (iter.hasNext()) { - Thread thread = (Thread) iter.next(); - //noinspection removal - thread.stop(); // http://java.sun.com/j2se/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html + if (parser != null) { + parser.interruptThreads(); } } @@ -1039,5 +1035,3 @@ public void uncaughtException(Thread t, Throwable e) { } } } - - diff --git a/src/dr/xml/XMLParser.java b/src/dr/xml/XMLParser.java index e252c3acec..b1010c8ab4 100644 --- a/src/dr/xml/XMLParser.java +++ b/src/dr/xml/XMLParser.java @@ -40,10 +40,15 @@ import org.xml.sax.helpers.DefaultHandler; import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.*; +import java.util.logging.Logger; public class XMLParser { + private static final Logger LOGGER = Logger.getLogger(XMLParser.class.getName()); + public static final String ID = XMLObject.ID; public static final String IDREF = "idref"; public static final String CONCURRENT = "concurrent"; @@ -105,7 +110,24 @@ public Iterator getParsers() { } public Iterator getThreads() { - return threads.iterator(); + synchronized (threadLock) { + return new ArrayList(threads).iterator(); + } + } + + public void interruptThreads() { + List currentThreads; + synchronized (threadLock) { + if (interruptRequested) { + return; + } + interruptRequested = true; + currentThreads = new ArrayList(threads); + } + for (Thread thread : currentThreads) { + requestThreadStop(thread); + thread.interrupt(); + } } public void storeObject(String name, Object object) { @@ -370,9 +392,13 @@ private Object convert(Element e, Class target, XMLObject parent, boolean run, b for (int i = 0; i < xo.getChildCount(); i++) { Object child = xo.getChild(i); if (child instanceof Runnable) { - Thread thread = new Thread((Runnable) child); + Runnable runnable = (Runnable) child; + Thread thread = new Thread(runnable); thread.start(); - threads.add(thread); + synchronized (threadLock) { + threads.add(thread); + threadTasks.put(thread, runnable); + } } else throw new XMLParseException("Concurrent element children must be runnable!"); } concurrent = false; @@ -387,13 +413,21 @@ private Object convert(Element e, Class target, XMLObject parent, boolean run, b if (obj instanceof Spawnable && !((Spawnable) obj).getSpawnable()) { ((Spawnable) obj).run(); } else { - Thread thread = new Thread((Runnable) obj); + Runnable runnable = (Runnable) obj; + Thread thread = new Thread(runnable); thread.start(); - threads.add(thread); + synchronized (threadLock) { + threads.add(thread); + threadTasks.put(thread, runnable); + } waitForThread(thread); } } - threads.removeAllElements(); + synchronized (threadLock) { + threadTasks.clear(); + threads.removeAllElements(); + interruptRequested = false; + } } return xo; @@ -551,9 +585,44 @@ private void waitForThread(Thread thread) { try { thread.join(); } catch (InterruptedException ie) { - // DO NOTHING + // Preserve interrupt status for callers that manage parser shutdown. + Thread.currentThread().interrupt(); + requestThreadStop(thread); + removeThreadTracking(thread); + return; } } + removeThreadTracking(thread); + } + + private void requestThreadStop(Thread thread) { + Runnable runnable; + synchronized (threadLock) { + runnable = threadTasks.get(thread); + } + if (runnable == null) { + return; + } + try { + Method method = runnable.getClass().getMethod("pleaseStop"); + method.invoke(runnable); + } catch (NoSuchMethodException ignored) { + // Expected: runnable does not implement pleaseStop(). + } catch (IllegalAccessException e) { + LOGGER.warning("Error while accessing pleaseStop() on " + + runnable.getClass().getName() + ": " + e.getMessage()); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + LOGGER.warning("Error while invoking pleaseStop() on " + + runnable.getClass().getName() + ": " + (cause == null ? e.getMessage() : cause.getMessage())); + } + } + + private void removeThreadTracking(Thread thread) { + synchronized (threadLock) { + threadTasks.remove(thread); + threads.remove(thread); + } } // //anonymous object store class @@ -612,7 +681,10 @@ public void addCitable(Citable citable) { private final Map parserStore = new TreeMap(new ParserComparator()); private final Map objectStore = new LinkedHashMap(); private final Map, List> citationStore = new LinkedHashMap, List>(); + private final Map threadTasks = new HashMap(); + private final Object threadLock = new Object(); private boolean concurrent = false; + private boolean interruptRequested = false; private XMLObject root = null; private boolean verbose = false; @@ -656,5 +728,3 @@ public int compare(String o1, String o2) { } } - -