Files
mercury/java/runtime/MercuryWorkerThread.java
Paul Bone a92b133e2c Fix a deadlock in the Java worker thread implementation
I noticed this bug when accidentally throwing a Mercury exception containing
some malformed Mercury data (a Java null pointer).  The Java worker thread
tried to report the exception, by calling back into Mercury but this
encountered a null pointer exception itself due to the bad data and failed
to de-register itself from the thread pool.  This caused the thread pool to
mistakingly think that a thread was still alive and it failed to terminate
the program.

The solution is tu ensure that the pool knows the thread has shutdown if any
exception occurs.

This bug isn't likely to occur in practice, throwing exceptions whose
message is null is unusual.

java/runtime/MercuryWorkerThread.java:
    As above.
2016-04-12 14:38:05 +10:00

97 lines
3.0 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;
try {
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);
} finally {
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;
}
}