[m-rev.] diff 3/3: [java] The thread pool now works when Mercury is used as

Julien Fischer jfischer at opturion.com
Tue Dec 16 12:01:42 AEDT 2014


Hi Paul,

On Tue, 16 Dec 2014, Paul Bone wrote:

> Branches: master
>
> The concept and names of the classes and new method have already been
> improved.  So no review is necessary.

Wanna bet? ;-)

> [java] The thread pool now works when Mercury is used as a library
>
> The thread pool code used in the Java backend was tied the execution of
> main/2.  However if Mercury is used as a library the thread pool won't have
> been started and threads created with thread.spawn would not be executed.
>
> This patch makes it possible to start and stop the thread pool independently of
> main/2 by calling startup() and shutdown().  These calls are called
> implicitly by calling runMain().  The thread pool can also be started on
> demand.
>
> This patch also adds the MercuryRuntime class, which now contains methods
> that may be called by users' Java code to interact with the Mercury runtime
> system, including a new finalise() method.
>
> java/runtime/MercuryThreadPool.java:
>    Add startup() method.
>
>    shutdown() method is now public and it's meaning has changed, it now
>    requests the shutdown rather than performing it.
>
>    Renamed some variables to make their meanings clearer.
>
> java/runtime/JavaInternal.java:
>    Initialise the ThreadPool and MercuryOptions objects on demand.
>
>    Make all members of this class static to avoid confusion.
>
>    Add a private constructor.
>
> java/runtime/MercuryRuntime.java:
>    Add methods that can be called by Mercury users to interact with the
>    runtime system.  Including a convenient finalise() method that does all
>    the finalisation.
>
> samples/java_interface/standalone_java/mercury_lib.m:
> samples/java_interface/standalone_java/JavaMain.java:
>    Extend the standalone Java example so that it makes use of threads: Add
>    a fibs function in Mercury that uses concurrency and therefore starts
>    the thread pool; call it from the Java code.
>
>    Use the new finalise() method from the MercuryRuntime class inside of a
>    finally block.
>
> samples/java_interface/standalone_java/Makefile:
>    Fix a minor error.
> ---
> java/runtime/JavaInternal.java                     |  49 ++++---
> java/runtime/MercuryRuntime.java                   |  59 +++++++++
> java/runtime/MercuryThreadPool.java                | 147 ++++++++++++++++++---
> .../java_interface/standalone_java/JavaMain.java   |  51 ++++---
> samples/java_interface/standalone_java/Makefile    |   2 +-
> .../java_interface/standalone_java/mercury_lib.m   |  42 ++++++
> 6 files changed, 294 insertions(+), 56 deletions(-)
> create mode 100644 java/runtime/MercuryRuntime.java
>
> diff --git a/java/runtime/JavaInternal.java b/java/runtime/JavaInternal.java
> index 307dfcf..2f9ac75 100644

...

> --- a/java/runtime/JavaInternal.java
> +++ b/java/runtime/JavaInternal.java
> @@ -1,5 +1,6 @@
> //
> // Copyright (C) 2001-2003, 2009 The University of Melbourne.
> +// 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.
> //
> @@ -9,33 +10,46 @@
> 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
> + * Internals and static objects for Mercury's runtime system on the Java
> + * backend.
> + * 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.
> + * The class also contains utility methods and other objects such as a
> + * reference to the thread pool.
> + *
> + * No instance of this class is ever created, all it's members and methods
> + * are static.
>  */

> public class JavaInternal {
>
> -    private static JavaInternal         instance;
> -
> +    /**
> +     * Private constructor.
> +     * This private constructor doesn't do anything and isn't called by
> +     * anyone.  It exists only to prevent people from creating an instance.
> +     */
>     private JavaInternal() {
> -        options = new MercuryOptions();
> -        options.process();
> -        thread_pool = new MercuryThreadPool(options.getNumProcessors());
>     }
>
> -    private MercuryThreadPool thread_pool;
> -    private MercuryOptions options;
> +    private static MercuryThreadPool    thread_pool = null;
> +    private static MercuryOptions       options = null;
>
> -    public static MercuryThreadPool getThreadPool() {
> -        return instance.thread_pool;
> +    public static synchronized MercuryThreadPool getThreadPool() {
> +        if (thread_pool == null) {
> +            thread_pool = new MercuryThreadPool(
> +                getOptions().getNumProcessors());
> +        }
> +        return thread_pool;
>     }
>
> -    public static MercuryOptions getOptions() {
> -        return instance.options;
> +    public static synchronized MercuryOptions getOptions() {
> +        if (options == null) {
> +            options = new MercuryOptions();
> +            options.process();
> +        }
> +        return options;
>     }
>
>     public static java.lang.String      progname;
> @@ -57,11 +71,12 @@ public class JavaInternal {
>     }
>
>     /**
> -     * Run the main task using the thread pool.
> +     * Run the main task.
> +     * The maun task is executed by the thread pool so that when it blocks

s/maun/main/

> +     * the thread pool is notified correctly.
>      */
>     public static void runMain(Runnable main)
>     {
> -        instance = new JavaInternal();
>         getThreadPool().runMain(main);
>     }
>
> diff --git a/java/runtime/MercuryRuntime.java b/java/runtime/MercuryRuntime.java
> new file mode 100644
> index 0000000..5eaa927
> --- /dev/null
> +++ b/java/runtime/MercuryRuntime.java
> @@ -0,0 +1,59 @@
> +//
> +// 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;
> +
> +/**
> + * Interface to the Mercury Runtime System for Java code.
> + *
> + * No instance of this class is ever created, all it's members and methods

s/it's/its/

> + * are static.
> + */
> +public class MercuryRuntime
> +{
> +    /**
> +     * Private constructor.
> +     * This private constructor doesn't do anything and isn't called by
> +     * anyone.  It exists only to prevent people from creating an instance.
> +     */
> +    private MercuryRuntime() {
> +    }
> +
> +    private static MercuryThreadPool    thread_pool = null;
> +
> +    /**
> +     * Return the thread pool, initalising it if required.
> +     * This does not start the thread pool.  It is started either when
> +     * startup() is called or automatically when the first task is
> +     * submitted.
> +     */
> +    public static synchronized MercuryThreadPool getThreadPool()
> +    {
> +        if (thread_pool == null) {
> +            thread_pool = new MercuryThreadPool(
> +                JavaInternal.getOptions().getNumProcessors());
> +        }
> +        return thread_pool;
> +    }
> +
> +    /**
> +     * Retrive the exit status stored in the IO state.
> +     */

s/IO/I\/O/

> +    public static int getExitStatus() {
> +        return JavaInternal.exit_status;
> +    }
> +
> +    /**
> +     * Finalise the runtime system.
> +     * This _must_ be cAlled at the normal end of any program.  Currently

Fix the capitalization in "cAlled".  Delete "Currently".


> +     * it runs finalisers and stops the thread pool.
> +     */
> +    public static void finalise() {
> +        JavaInternal.run_finalisers();
> +        getThreadPool().shutdown();
> +    }
> +}
> +
> diff --git a/java/runtime/MercuryThreadPool.java b/java/runtime/MercuryThreadPool.java
> index 4b63460..be121a2 100644
> --- a/java/runtime/MercuryThreadPool.java
> +++ b/java/runtime/MercuryThreadPool.java
> @@ -86,12 +86,19 @@ public class MercuryThreadPool
>      * Tasks
>      */
>     private Deque<Task>             tasks;
> -    private boolean                 should_shutdown;
>     private long                    num_tasks_submitted;
>     private long                    num_tasks_completed;
>     private Lock                    tasks_lock;
>     private Condition               thread_wait_for_task_condition;
>
> +    // Has a shutdown request been received (protected by tasks_lock)
> +    private boolean                 shutdown_request;
> +    // True if worker threads should exit (the pool is shutting down).
> +    private boolean                 shutdown_now;
> +    // True if the thread pool is running (including starting up and
> +    // shutting down).
> +    private boolean                 running;
> +
>     /*
>      * Main loop condition.
>      */

...

  @@ -439,13 +452,16 @@ public class MercuryThreadPool
>     }
>
>     /**
> -     * Run the thread pool.  This is usually called by runMain()
> +     * Run the thread pool.
> +     * The calling thread is used to "run" the thread pool.  It's main job

s/It's/Its/

> +     * is to keep the correct number of worker threads alive.  It does not
> +     * return until the thread pool is stopped (with a call to shutdown()).
> +     * run() is usually called by runMain(), and shutdown() is usually
> +     * called by the main task itself.
>      */
>     public void run()
>     {
> -        boolean done = false;
> -        long    num_tasks_submitted;
> -        long    num_tasks_completed;
> +        boolean will_shutdown = false;
>         boolean tasks_locked = false;
>         boolean main_loop_locked = false;
>

...

> -        shutdown();
> +        /*
> +         * Shutdown
> +         */
> +        tasks_lock.lock();
> +        try {
> +            shutdown_now = true;
> +            thread_wait_for_task_condition.signalAll();
> +            running = false;
> +        } finally {
> +            tasks_lock.unlock();
> +        }
>     }
>
> -    protected void shutdown()
> +    /**
> +     * Start the thread pool in it's own thread.

s/it's/its/

> +     * Normally the thread pool ie executed directly by the main thread.
> +     * However, when Mercury is used as a library by a native Java
> +     * application this is not true, and the thread pool runs in a thread of
> +     * it's own.
> +     */
> +    public MercuryThread startup()
>     {
> +        MercuryThread thread;
> +
>         tasks_lock.lock();
>         try {
> -            should_shutdown = true;
> -            thread_wait_for_task_condition.signalAll();
> +            if (running) {
> +                return null;
> +            }
> +            running = true;
> +        } finally {
> +            tasks_lock.unlock();
> +        }
> +
> +        startupInitialThreads();
> +        thread = thread_factory.newThread(new Runnable() {
> +            public void run() {
> +                MercuryThreadPool.this.startupInitialThreads();
> +                MercuryThreadPool.this.run();
> +            }
> +        });
> +        thread.start();
> +        return thread;
> +    }
> +
> +    /**
> +     * Request that the thread pool shutdown.
> +     * This method does not wait for the thread pool to shutdown, it's an
> +     * asychronous signal.  The thread pool will shutdown if: shutdown() has
> +     * been called (implicitly when running as an application) and there are

What do you mean by "running as an application"?

> +     * no remaining tasks either queued or running (spawn_native tasks are
> +     * not included).  The requirement that the process does not exit until
> +     * all tasks have finish is maintained by the JVM.
> +     */
> +    public boolean shutdown()
> +    {
> +        tasks_lock.lock();
> +        try {
> +            if (running && !shutdown_request) {
> +                shutdown_request = true;
> +            } else {
> +                return false;
> +            }
>         } finally {
>             tasks_lock.unlock();
>         }
>
> +        signalMainLoop();
> +        return true;
>     }


> diff --git a/samples/java_interface/standalone_java/JavaMain.java b/samples/java_interface/standalone_java/JavaMain.java
> index cfb7572..8cc9f31 100644
> --- a/samples/java_interface/standalone_java/JavaMain.java
> +++ b/samples/java_interface/standalone_java/JavaMain.java
> @@ -1,10 +1,10 @@
> // vim: ts=4 sw=4 et
>
> -// The JavaInternal class in the jmercury.runtime package provides various
> +// The MercuryRuntime class in the jmercury.runtime package provides various
> // Mercury runtime services that we may require.
> // All Mercury runtime and generated Java code lives in the jmercury package.
> //
> -import jmercury.runtime.JavaInternal;
> +import jmercury.runtime.MercuryRuntime;
>
> // The mercury_lib class is generated by the compiler when we build
> // mercury_lib library.
> @@ -20,9 +20,36 @@ public class JavaMain {
>         // We do not need to do anything to initialise the Java version of the
>         // Mercury runtime.  It will be automatically initialised as the
>         // relevant classes are loaded by the JVM.
> -
>         out.println("JavaMain: start main");
>
> +        try {
> +            runProgram(args);
> +        } finally {
> +            // When we have finished calling Mercury procedures then we need to
> +            // tell the Mercury Runtime that we've finished using it.
> +            // This invokes any finalisers specified using ':- finalise'
> +            // declarations in the set of Mercury libraries we are using.  It
> +            // also tells the thread pool to shutdown, if the thread pool is not
> +            // runnuing then this does nothing.
> +            // The static method finalise() in the MercuryRuntime class does
> +            // this.
> +            //
> +            MercuryRuntime.finalise();
> +
> +            // The Mercury exit status (as set by io.set_exit_status/1) may
> +            // be read from the MercuryRuntime class.
> +            //
> +            out.println("JavaMain: Mercury exit status = "
> +                + MercuryRuntime.getExitStatus());
> +
> +            out.println("JavaMain: end main");
> +        }
> +
> +        System.exit(MercuryRuntime.getExitStatus());
> +    }

You should explain *why* it is necessary to wrap the calls to Mercury
in this try - finally block and the consequences of not doing so in the
case of an uncaught Mercury exception.


> +
> +    public static void runProgram(String[] args) {
> +
>         // This is a call to an exported Mercury procedure that does some I/O.
>         // The mercury_lib class contains a static method for each procedure
>         // that is foreign exported to Java.
> @@ -33,21 +60,7 @@ public class JavaMain {
>         //
>         out.println("3^3 = " + mercury_lib.cube(3));
>
> -        // When we have finished calling Mercury procedures then we need to
> -        // invoke any finalisers specified using ':- finalise' declarations in
> -        // the set of Mercury libraries we are using.
> -        // The static method run_finalisers() in the JavaInternal class does
> -        // this.  It will also perform any Mercury runtime finalisation that
> -        // may be needed.
> -        //
> -        JavaInternal.run_finalisers();
> -
> -        // The Mercury exit status (as set by io.set_exit_status/1) may be read
> -        // from the static field 'exit_status' in the JavaInternal class.
> -        //
> -        out.println("JavaMain: Mercury exit status = "
> -            + JavaInternal.exit_status);
> -
> -        out.println("JavaMain: end main");
> +        // Try a parallelised Mercury function.
> +        out.println("fibs(40) = " + mercury_lib.fibs(40));
>    }
> }
> diff --git a/samples/java_interface/standalone_java/Makefile b/samples/java_interface/standalone_java/Makefile
> index 407104a..8c3bf94 100644
> --- a/samples/java_interface/standalone_java/Makefile
> +++ b/samples/java_interface/standalone_java/Makefile
> @@ -14,7 +14,7 @@ MER_JARS = $(MER_LIB_DIR)/mer_std.jar:$(MER_LIB_DIR)/mer_rt.jar
> .PHONY: all
> all: run
>
> -JavaMain.class: JavaMain.java mercury_lib.jar
> +JavaMain.class: JavaMain.java libmercury_lib.jar
> 	$(JAVAC) JavaMain.java -cp $(MER_JARS):Mercury/classs -d .

Where is libmercury_lib.jar coming from?  In the Java grade, the jar
generated for libraries will be named mercury_lib.jar.

> libmercury_lib.jar: mercury_lib.m
> diff --git a/samples/java_interface/standalone_java/mercury_lib.m b/samples/java_interface/standalone_java/mercury_lib.m
> index 4fcdacc..efc52c3 100644
> --- a/samples/java_interface/standalone_java/mercury_lib.m
> +++ b/samples/java_interface/standalone_java/mercury_lib.m
> @@ -17,6 +17,11 @@
>     %
> :- func cube(int) = int.
>
> +    % fibs(N) returns the Nth fibbonanci number using a parallelised naive
> +    % algorithm.

s/fibbonanci/Fibonacci/

Cheers,
Julien.



More information about the reviews mailing list