Files
mercury/java/runtime/MercuryWorkerThread.java
Paul Bone 5c4671ce9b [java] Fix non-termination when main/2 throws an exception
When main/2 throws an exception we did not properly shutdown the thread pool
and therefore the JVM would not shut down.  Simply calling shutdown() in a
finally block is insufficient because then the primordial thread may finish
before the worker thread is able to report the exception thrown by main/2.
This doesn't seem right because the JVM is supposed to wait for all the
non-daemon threads to finish before it exits.  I suspect that the primordial
thread is closing stdout and stderr as it exits and therefore the exception
is never seen, but I don't know.

This change fixes the issue by ensuring that shutdown() is always called (in
a finally block) and that the main thread waits for the thread pool to
shutdown before it exits.

java/runtime/MercuryThreadPool.java:
    runMain() will not exit until the worker threads have exited.

    Create a new method waitForShutdown() that will wait for the thread pool
    to shutdown.

    Signal the main thread when a worker thread exits.

java/runtime/MercuryWorkerThread.java:
    Worker threads now exit if their task raises an unhanded exception.

java/runtime/MercuryRuntime.java:
    Allow standalone programs to have the same behavour as programs whose
    entrypoint is written in Mercury.
2014-12-30 17:07:35 +11:00

95 lines
2.8 KiB
Java

//
// Copyright (C) 2014 The Mercury Team
// This file may only be copied under the terms of the GNU Library General
// Public License - see the file COPYING.LIB in the Mercury distribution.
//
package jmercury.runtime;
/**
* Threads for the Mercury code running in Java.
*/
public class MercuryWorkerThread extends MercuryThread
{
private MercuryThreadPool pool;
private ThreadStatus status;
/**
* Construct a new MercuryThread with the given ID and runnable.
* @param pool The Mercury thread pool.
* @param id A numeric identifier (should be unique).
*/
public MercuryWorkerThread(MercuryThreadPool pool, int id)
{
super("Mercury Worker Thread", id);
this.pool = pool;
this.status = ThreadStatus.OTHER;
}
/**
* Run.
* The worker thread executes tasks that it retrives from the pool.
*/
public void run()
{
Task task;
do {
task = null;
try {
if (status != ThreadStatus.IDLE) {
setStatus(ThreadStatus.IDLE);
}
task = pool.workerGetTask();
}
catch (InterruptedException e) {
/*
** A worker thread has no semantics for this, so we continue
** looping.
*/
continue;
}
if (task != null) {
try {
setStatus(ThreadStatus.WORKING);
task.run();
pool.taskDone(task);
} catch (jmercury.runtime.Exception e) {
// The task threw a Mercury exception.
pool.taskFailed(task, e);
JavaInternal.reportUncaughtException(e);
// Make the thread exit after throwing an exception.
break;
} catch (Throwable e) {
// Some other error occured. bail out.
System.err.println("Uncaught exception: " + e.toString());
System.err.println(e.getMessage());
e.printStackTrace();
System.exit(1);
} finally {
setStatus(ThreadStatus.OTHER);
}
}
} while (task != null);
pool.threadShutdown(this, status);
}
protected void setStatus(ThreadStatus new_status) {
pool.updateThreadCounts(status, new_status);
status = new_status;
}
public void blocked() {
pool.updateThreadCounts(status, ThreadStatus.BLOCKED);
status = ThreadStatus.BLOCKED;
}
public void running() {
pool.updateThreadCounts(status, ThreadStatus.WORKING);
status = ThreadStatus.WORKING;
}
}