[m-rev.] for review: no native code for java

Peter Wang novalazy at gmail.com
Fri Aug 14 11:50:05 AEST 2009


Branches: main

Make the Java backend not use native code to implement any standard library
procedures.  The only such procedures were related to benchmarking and timing,
which aren't very important so I just make a best effort using methods that
the Java standard library provides.

library/benchmarking.m:
        Implement `benchmarking.report_stats' for Java as closely as possible
        without native code.  Add real time output.

        Add `benchmarking.ML_initialise' to remember the time when the
        program starts up.

compiler/mlds_to_java.m:
        Make the main wrapper call `benchmarking.ML_initialise' at startup.

library/time.m:
        Implement `time.clock', `time.times', `time.clocks_per_sec',
        `time.clk_tck' as closely as possible without native code.

library/Mmakefile:
        Comment out commands to build and install Native.so.

java/runtime/Native.c:
        Even though this is not used any more, update the function names for
        the "jmercury" package prefix.

diff --git a/compiler/mlds_to_java.m b/compiler/mlds_to_java.m
index 4622db5..58b892a 100644
--- a/compiler/mlds_to_java.m
+++ b/compiler/mlds_to_java.m
@@ -1673,6 +1673,7 @@ write_main_driver(Indent, ClassName, !IO) :-
         "jmercury.runtime.JavaInternal.progname = """ ++ ClassName ++ """;",
         "jmercury.runtime.JavaInternal.args = args;",
         "jmercury.runtime.JavaInternal.exit_status = 0;",
+        "benchmarking.ML_initialise();",
         "try {",
         "   " ++ ClassName ++ ".main_2_p_0();",
         "   jmercury.runtime.JavaInternal.run_finalisers();",
diff --git a/java/runtime/Native.c b/java/runtime/Native.c
index 570b167..977ff6c 100644
--- a/java/runtime/Native.c
+++ b/java/runtime/Native.c
@@ -15,14 +15,14 @@
  * Method:    clock
  * Signature: ()I
  */
-JNIEXPORT jint JNICALL Java_mercury_runtime_Native_clock(JNIEnv *, jclass);
+JNIEXPORT jint JNICALL Java_jmercury_runtime_Native_clock(JNIEnv *, jclass);
 
 /*
  * Class:     Native
  * Method:    clocks_per_sec
  * Signature: ()I
  */
-JNIEXPORT jint JNICALL Java_mercury_runtime_Native_clocks_1per_1sec(
+JNIEXPORT jint JNICALL Java_jmercury_runtime_Native_clocks_1per_1sec(
 		JNIEnv *, jclass);
 
 /*
@@ -30,7 +30,7 @@ JNIEXPORT jint JNICALL Java_mercury_runtime_Native_clocks_1per_1sec(
  * Method:    times
  * Signature: ()[I
  */
-JNIEXPORT jintArray JNICALL Java_mercury_runtime_Native_times(
+JNIEXPORT jintArray JNICALL Java_jmercury_runtime_Native_times(
 		JNIEnv *, jclass);
 
 /*
@@ -38,14 +38,14 @@ JNIEXPORT jintArray JNICALL Java_mercury_runtime_Native_times(
  * Method:    clk_tck
  * Signature: ()I
  */
-JNIEXPORT jint JNICALL Java_mercury_runtime_Native_clk_1tck(JNIEnv *, jclass);
+JNIEXPORT jint JNICALL Java_jmercury_runtime_Native_clk_1tck(JNIEnv *, jclass);
 
 /*
  * Class:     Native
  * Method:    get_user_cpu_milliseconds
  * Signature: ()I
  */
-JNIEXPORT jint JNICALL Java_mercury_runtime_Native_get_1user_1cpu_1milliseconds(
+JNIEXPORT jint JNICALL Java_jmercury_runtime_Native_get_1user_1cpu_1milliseconds(
 		JNIEnv *, jclass);
 
 #include "mercury_imp.h"
@@ -63,17 +63,17 @@ JNIEXPORT jint JNICALL Java_mercury_runtime_Native_get_1user_1cpu_1milliseconds(
 #endif
 
 JNIEXPORT jint JNICALL
-Java_mercury_runtime_Native_clock(JNIEnv *env, jclass obj) {
+Java_jmercury_runtime_Native_clock(JNIEnv *env, jclass obj) {
 	return (MR_Integer) clock();
 }
 
 JNIEXPORT jint JNICALL
-Java_mercury_runtime_Native_clocks_1per_1sec(JNIEnv *env, jclass obj) {
+Java_jmercury_runtime_Native_clocks_1per_1sec(JNIEnv *env, jclass obj) {
 	return CLOCKS_PER_SEC;
 }
 
 JNIEXPORT jintArray JNICALL
-Java_mercury_runtime_Native_times(JNIEnv *env, jclass obj) {
+Java_jmercury_runtime_Native_times(JNIEnv *env, jclass obj) {
 	jint		intarray[5];
 	jintArray	result;
 
@@ -97,7 +97,7 @@ Java_mercury_runtime_Native_times(JNIEnv *env, jclass obj) {
 	return result;
 }
 
-JNIEXPORT jint JNICALL Java_mercury_runtime_Native_clk_1tck(
+JNIEXPORT jint JNICALL Java_jmercury_runtime_Native_clk_1tck(
 		JNIEnv *env, jclass obj)
 {
 #if defined(MR_CLOCK_TICKS_PER_SECOND)
@@ -108,7 +108,7 @@ JNIEXPORT jint JNICALL Java_mercury_runtime_Native_clk_1tck(
 }
 
 JNIEXPORT jint JNICALL
-Java_mercury_runtime_Native_get_1user_1cpu_1milliseconds(
+Java_jmercury_runtime_Native_get_1user_1cpu_1milliseconds(
 		JNIEnv *env, jclass obj)
 {
 	return MR_get_user_cpu_milliseconds();
diff --git a/library/Mmakefile b/library/Mmakefile
index a8fc2b8..293d08a 100644
--- a/library/Mmakefile
+++ b/library/Mmakefile
@@ -284,10 +284,11 @@ jars:	classes
 	$(RM) $(STD_LIB_NAME).classes
 	$(JAR) $(JAR_CREATE_FLAGS) $(RT_LIB_NAME).jar jmercury/runtime/*.class
 	$(JAR) i $(RT_LIB_NAME).jar
-	-+cd jmercury/runtime && mmake $(NATIVE_SO)
-	-cp jmercury/runtime/$(NATIVE_SO) .
+	# -+cd jmercury/runtime && mmake $(NATIVE_SO)
+	# -cp jmercury/runtime/$(NATIVE_SO) .
 
-# This shared object is needed to run some of the standard library methods.
+# This shared object was used to implement some standard library methods,
+# but currently not.
 NATIVE_SO = Native.$(EXT_FOR_SHARED_LIB)
 
 #-----------------------------------------------------------------------------#
@@ -536,7 +537,7 @@ ifneq (,$(findstring java,$(GRADE)))
 install_library: jars
 	mkdir -p $(INSTALL_JAVA_LIBRARY_DIR)
 	cp $(JARS) $(INSTALL_JAVA_LIBRARY_DIR)
-	-cp $(NATIVE_SO) $(INSTALL_JAVA_LIBRARY_DIR)
+	# -cp $(NATIVE_SO) $(INSTALL_JAVA_LIBRARY_DIR)
 
 else
 
diff --git a/library/benchmarking.m b/library/benchmarking.m
index ac9b7fd..d4d1ad9 100644
--- a/library/benchmarking.m
+++ b/library/benchmarking.m
@@ -27,6 +27,10 @@
     % some memory and time usage statistics about the time period since
     % the last call to report_stats to stderr.
     %
+    % Note: in Java, this reports usage of the calling thread.  You will get
+    % non-sensical results if the previous call to `report_stats' was from a
+    % different thread.
+    %
     % Note: in Erlang, the benchmark_* procedures will change the apparent time
     % of the last call to report_stats.
     %
@@ -684,26 +688,45 @@ ML_memory_profile_compare_final(const void *i1, const void *i2)
 
 :- pragma foreign_code("Java",
 "
-private static int time_at_start    = 0;
-private static int time_at_last_stat    = 0;
+private static int user_time_at_start = 0;
+private static int user_time_at_last_stat = 0;
+private static long real_time_at_start;
+private static long real_time_at_last_stat;
 
-static {
-    if (jmercury.runtime.Native.isAvailable()) {
-        time_at_start = jmercury.runtime.Native.get_user_cpu_milliseconds();
-        time_at_last_stat = time_at_start;
-    }
+public static void
+ML_initialise()
+{
+    /*
+    ** Class initialisation may be delayed so main() must explicitly initialise
+    ** these variables at startup, otherwise the first call to `report_stats'
+    ** will show the wrong elapsed time.
+    */
+    real_time_at_start = System.currentTimeMillis();
+    real_time_at_last_stat = real_time_at_start;
 }
 
 private static void
-ML_report_stats() {
-    int time_at_prev_stat = time_at_last_stat;
-    time_at_last_stat = get_user_cpu_milliseconds_1_p_0();
-
-    System.err.print(""[Time: "" +
-        ((time_at_last_stat - time_at_prev_stat) / 1000.0) +
-        "", "" +
-        ((time_at_last_stat - time_at_start) / 1000.0)
-        );
+ML_report_stats()
+{
+    int user_time_at_prev_stat = user_time_at_last_stat;
+    user_time_at_last_stat = ML_get_user_cpu_milliseconds();
+
+    long real_time_at_prev_stat = real_time_at_last_stat;
+    real_time_at_last_stat = System.currentTimeMillis();
+
+    System.err.print(
+        ""[User time: +"" +
+        ((user_time_at_last_stat - user_time_at_prev_stat) / 1000.0) +
+        ""s, "" +
+        ((user_time_at_last_stat - user_time_at_start) / 1000.0) +
+        ""s"");
+
+    System.err.print(
+        "" Real time: +"" +
+        ((real_time_at_last_stat - real_time_at_prev_stat) / 1000.0) +
+        ""s, "" +
+        ((real_time_at_last_stat - real_time_at_start) / 1000.0) +
+        ""s"");
 
     /*
     ** XXX At this point there should be a whole bunch of memory usage
@@ -715,7 +738,8 @@ ML_report_stats() {
 }
 
 private static void
-ML_report_full_memory_stats() {
+ML_report_full_memory_stats()
+{
     /*
     ** XXX The support for this predicate is even worse.  Since we don't have
     ** access to memory usage statistics, all you get here is an apology.
@@ -849,6 +873,9 @@ repeat(N) :-
 
 :- impure pred get_user_cpu_milliseconds(int::out) is det.
 
+:- pragma foreign_export("Java", get_user_cpu_milliseconds(out),
+    "ML_get_user_cpu_milliseconds").
+
 :- pragma foreign_proc("C",
     get_user_cpu_milliseconds(Time::out),
     [will_not_call_mercury],
@@ -870,14 +897,19 @@ repeat(N) :-
 
 :- pragma foreign_proc("Java",
     get_user_cpu_milliseconds(Time::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, may_not_duplicate],
 "
-    if (jmercury.runtime.Native.isAvailable()) {
-        Time = jmercury.runtime.Native.get_user_cpu_milliseconds();
-    } else {
-        throw new java.lang.RuntimeException(
-            ""get_user_cpu_milliseconds is not implemented in pure Java."" +
-            ""Native dynamic link library is required."");
+    try {
+        java.lang.management.ThreadMXBean bean =
+            java.lang.management.ManagementFactory.getThreadMXBean();
+        long nsecs = bean.getCurrentThreadUserTime();
+        if (nsecs == -1) {
+            Time = -1;
+        } else {
+            Time = (int) (nsecs / 1000000L);
+        }
+    } catch (java.lang.UnsupportedOperationException e) {
+        Time = -1;
     }
 ").
 
diff --git a/library/time.m b/library/time.m
index 008d034..4d5aad5 100644
--- a/library/time.m
+++ b/library/time.m
@@ -88,6 +88,8 @@
     % cannot be obtained, this procedure will throw a time_error exception.
     % To obtain a time in seconds, divide Result by `time.clocks_per_sec'.
     %
+    % On Java the elapsed time for the calling thread is returned.
+    %
 :- pred time.clock(clock_t::out, io::di, io::uo) is det.
 
     % time.clocks_per_sec:
@@ -124,7 +126,8 @@
     % On non-POSIX systems that do not support this functionality,
     % this procedure may simply always throw an exception.
     %
-    % Currently on Win32 the child part of 'tms' is always zero.
+    % On Java the times for the calling thread are returned.
+    % On Win32 and Java the child part of 'tms' is always zero.
     %
 :- pred time.times(tms::out, clock_t::out, io::di, io::uo) is det.
 
@@ -279,13 +282,14 @@ time.clock(Result, !IO) :-
     time.c_clock(Ret::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io],
 "
-    if (jmercury.runtime.Native.isAvailable()) {
-        Ret = jmercury.runtime.Native.clock();
+    java.lang.management.ThreadMXBean bean =
+        java.lang.management.ManagementFactory.getThreadMXBean();
+    long nsecs = bean.getCurrentThreadCpuTime();
+    if (nsecs == -1) {
+        Ret = -1;
     } else {
-        throw new java.lang.RuntimeException(
-            ""time.clock is not implemented "" +
-            ""in pure Java.  Native dynamic link "" +
-            ""library is required."");
+        /* This must match the definition of clocks_per_sec. */
+        Ret = (int) (nsecs / 1000L);
     }
 ").
 
@@ -310,14 +314,8 @@ time.clock(Result, !IO) :-
     time.clocks_per_sec = (Ret::out),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
-    if (jmercury.runtime.Native.isAvailable()) {
-        Ret = jmercury.runtime.Native.clocks_per_sec();
-    } else {
-        throw new java.lang.RuntimeException(
-            ""time.clocks_per_sec is not implemented "" +
-            ""in pure Java.  Native dynamic link "" +
-            ""library is required."");
-    }
+    /* Emulate the POSIX value. */
+    Ret = 1000000;
 ").
 
 %-----------------------------------------------------------------------------%
@@ -390,27 +388,31 @@ time.times(Tms, Result, !IO) :-
 :- pragma foreign_proc("Java",
     time.c_times(Ret::out, Ut::out, St::out, CUt::out, CSt::out,
         _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io, may_not_duplicate],
 "
-    if (jmercury.runtime.Native.isAvailable()) {
-        int[] times = jmercury.runtime.Native.times();
-        if (times != null) {
-            Ret = times[0];
-            Ut  = times[1];
-            St  = times[2];
-            CUt = times[3];
-            CSt = times[4];
+    /* We can only keep the lower 31 bits of the timestamp. */
+    Ret = (int) (System.currentTimeMillis() & 0x7fffffff);
+
+    try {
+        java.lang.management.ThreadMXBean bean =
+            java.lang.management.ManagementFactory.getThreadMXBean();
+        long user_nsecs = bean.getCurrentThreadUserTime();
+        long cpu_nsecs = bean.getCurrentThreadCpuTime();
+        if (user_nsecs == -1 || cpu_nsecs == -1) {
+            Ut = -1;
+            St = -1;
         } else {
-            throw new java.lang.RuntimeException(
-                ""time_times failed to construct "" +
-                ""integer array"");
+            /* These units must match the definition of clk_tck. */
+            Ut = (int) (user_nsecs / 1000000L);
+            St = (int) ((cpu_nsecs - user_nsecs) / 1000000L);
         }
-    } else {
-        throw new java.lang.RuntimeException(
-            ""time.times is not implemented "" +
-            ""in pure Java.  Native dynamic link "" +
-            ""library is required."");
+    } catch (java.lang.UnsupportedOperationException e) {
+        Ut = -1;
+        St = -1;
     }
+
+    CUt = 0;
+    CSt = 0;
 ").
 
 %-----------------------------------------------------------------------------%
@@ -448,14 +450,11 @@ time.c_clk_tck = -1.   % default is to throw an exception.
     time.c_clk_tck = (Ret::out),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
-    if (jmercury.runtime.Native.isAvailable()) {
-        Ret = jmercury.runtime.Native.clk_tck();
-    } else {
-        throw new java.lang.RuntimeException(
-            ""time.clk_tck is not implemented "" +
-            ""in pure Java.  Native dynamic link "" +
-            ""library is required."");
-    }
+    /*
+    ** We use System.currentTimeMillis() to return elapsed time so say that
+    ** there are 1000 clock ticks per second.
+    */
+    Ret = 1000;
 ").
 
 %-----------------------------------------------------------------------------%

--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list