mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 13:23:53 +00:00
Thread pools are often used to reduce poor performance on embarrassingly
parallel work loads. This isn't immediately necessary on the Java backend
however I've implemented it because:
+ It will be required when we implement parallel conjunctions for the
Java backend.
+ I want to implement parallel profiling on the Java backend and don't
want to have to implement this once now, and then re-implement it after
introducing thread pools later.
We want the thread pool to generally restrict the number of threads that are
in use, this reduces overheads. However, when one or more tasks become
blocked then it can be desirable to create extra threads, this helps ensure
that all processors are kept busy and that thread pooling doesn't contribute
to any deadlocks itself. The implementation is a work in prograss and
currently does not implement this second feature.
Java's API provides several different thread pools, see
java.util.concurrent.Executors, none of which are suitable. Specifically
the fixed thread pool is unsuitable as we want to be able to temporarily
exceed the normal number of threads as explained above; and the cached
thread pools, which are also very similar to the ThreadPoolExecutor class,
do not implement the correct algorithm for determining when a new thread
should be created (they can still perform poorly for embarassingly parallel
workloads). Additionally we cannot instrument this code as easily for
parallel profiling.
These changes alter the behaviour of Mercury threads on the Java backend in
two ways, they now behave more correctly and more like threads on the C
backends.
+ If a thread throws an exception it is now reported and the program is
aborted. Previously it was ignored and let pass to the Java runtime
where I assume it was reported.
+ The program now exits only after all threads have exited.
The ThreadPool will automatically detect the number of threads to use, or if
the -P flag is given in the MERCURY_OPTIONS environment variable it will
honor that.
java/runtime/MercuryThread.java:
java/runtime/MercuryThreadPool.java:
java/runtime/MercuryWorkerThread.java:
java/runtime/Task.java:
java/runtime/ThreadStatus.java:
These new classes make up the thread pool. A MercuryThread is an
abstract class for Mercury threads, MercuryWorkerThread is a concrete
subclass of MercuryThread which includes the worker thread behaviour.
A Task is a computation/closure that has not yet been started, it
provides some methods not available in Java's generic Runnable and
Callable classes. The others should be self-explanatory and all files
contain documentation.
java/runtime/Getopt.java:
java/runtime/MercuryOptions.java:
Parse the MERCURY_OPTIONS environment variable for the -P flag.
java/runtime/JavaInternal.java:
Add support for handling Mercury exceptions, this is used in case a
worker thread's task (a Mercury thread) throws an exception.
compiler/mlds_to_java.m:
The main method of the main Java class of an application now starts and
uses the thread pool to execute main/2.
library/exception.m:
Export exception reporting code to the Java runtime system.
library/thread.m:
Use the thread pool for thread.spawn.
122 lines
3.6 KiB
Java
122 lines
3.6 KiB
Java
//
|
|
// Copyright (C) 2001-2003, 2009 The University of Melbourne.
|
|
// 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.
|
|
//
|
|
// All modifications to this file will require changes to:
|
|
// compiler/mlds_to_java.m
|
|
|
|
package jmercury.runtime;
|
|
|
|
/**
|
|
* Internals for Mercury's runtime system on the Java backend.
|
|
* At the moment this class is used to store the main module's name (progname),
|
|
* command line arguments and the exit status. We can't put them in one of the
|
|
* library modules because we need to hold them in a class variable in a top
|
|
* level class.
|
|
*
|
|
* The class also contains utility methods.
|
|
*/
|
|
public class JavaInternal {
|
|
|
|
private static JavaInternal instance;
|
|
|
|
private JavaInternal() {
|
|
options = new MercuryOptions();
|
|
options.process();
|
|
thread_pool = new MercuryThreadPool(options.getNumProcessors());
|
|
}
|
|
|
|
private MercuryThreadPool thread_pool;
|
|
private MercuryOptions options;
|
|
|
|
public static MercuryThreadPool getThreadPool() {
|
|
return instance.thread_pool;
|
|
}
|
|
|
|
public static MercuryOptions getOptions() {
|
|
return instance.options;
|
|
}
|
|
|
|
public static java.lang.String progname;
|
|
public static java.lang.String[] args;
|
|
public static int exit_status;
|
|
private static ExceptionReporter exception_reporter;
|
|
|
|
private static java.util.List<Runnable> finalisers
|
|
= new java.util.ArrayList<Runnable>();
|
|
|
|
public static void register_finaliser(Runnable hook) {
|
|
finalisers.add(hook);
|
|
}
|
|
|
|
public static void run_finalisers() {
|
|
for (Runnable r : finalisers) {
|
|
r.run();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run the main task using the thread pool.
|
|
*/
|
|
public static void runMain(Runnable main)
|
|
{
|
|
instance = new JavaInternal();
|
|
getThreadPool().runMain(main);
|
|
}
|
|
|
|
/**
|
|
* Set the exception reporter if one is not set already.
|
|
* The specified object will be used by reportUncaughtException to
|
|
* report exceptions. See the comment for reportUncaughtException()
|
|
% @param reporter The new exception reporter object.
|
|
*/
|
|
public static void setExceptionReporter(ExceptionReporter reporter)
|
|
{
|
|
if (null == exception_reporter) {
|
|
exception_reporter = reporter;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report uncaught exceptions.
|
|
* This reports exceptions using the exception reporter object, if it is
|
|
* set. Otherwise we make a best effort at reporting it.
|
|
*
|
|
* Exception reporting code is written in exception.m which we cannot
|
|
* link to from here. When that code starts it should set an exception
|
|
* reporter object.
|
|
*/
|
|
public static void reportUncaughtException(jmercury.runtime.Exception e)
|
|
{
|
|
if (null != exception_reporter) {
|
|
exception_reporter.reportUncaughtException(e);
|
|
} else {
|
|
System.out.flush();
|
|
System.err.println("Uncaught exception: " + e.getMessage());
|
|
System.err.flush();
|
|
}
|
|
|
|
if (shouldPrintStackTrace()) {
|
|
e.printStackTrace(System.err);
|
|
}
|
|
if (exit_status == 0) {
|
|
exit_status = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Should we print Java stack traces when we catch Mercury exceptions?
|
|
*/
|
|
public static boolean shouldPrintStackTrace() {
|
|
return System.getenv("MERCURY_SUPPRESS_STACK_TRACE") == null;
|
|
}
|
|
|
|
/**
|
|
* Interface for reporting exceptions.
|
|
*/
|
|
public interface ExceptionReporter {
|
|
public void reportUncaughtException(jmercury.runtime.Exception e);
|
|
}
|
|
}
|