Thursday, April 23, 2009

Busy times etc.

Lately I have been working full time with my LLVM thesis. Also, in terms of life, it is harsh times currently. Everybody is mortal, unfortunately - or fortunately?

Now something completely different: If a Finn ever happens to read my blog, and is interested in Mika Waltari's life and work, then I must recommend Panu Rajala's biography of Waltari: Unio Mystica. It is a refreshing story of a creative, turbulent, storming mind.

Now, I want to graduate; afterwards: work hard, learn, sense, feel, enjoy and suffer.

Thursday, April 9, 2009

Intuitive Clojure

I just downloaded and tried Clojure for the first time. With a little help from clojure dot org, I managed to write the following snippet that demonstrates the use of tail calls, Java interoperation, exceptions and a bit of I/O. Of course, as is typical for lisps, it just worked the first time.

The program simplifies div expressions. The input is given in format <numerator> <denominator>, for example, 4/2, is given as 4 2.


 1 (defn gcd [a b]
 2     (if (= a b)
 3         a
 4         (if (> a b)
 5            (recur (- a b) b)
 6            (recur a (- b a)))))
 7
 8 (defn run []
 9     (let [line (read-line)
10           args (.split line " ")
11           len (alength args)]
12         (if (< len 2)
13            (println "Expected two arguments.")
14            (try (let [a (Integer/parseInt (aget args 0))
15                       b (Integer/parseInt (aget args 1))
16                       gcd (gcd a b)]
17                    (println a "/" b "=" (/ a gcd) "/" (/ b gcd)))
18                (catch NumberFormatException
19                    ex
20                    (println "Expected natural numbers."))))))
21 (run)

Wednesday, April 8, 2009

Lessons on JNI and dynamic libraries in Linux

I was experimenting for the first time with two things: The Java Native Library (JNI) and home-made dynamically linked libraries in Linux. It took some awkward moments until it all worked due to amateurish handling of the subject, and some bad attention. Still, it was rather amusing, and shall be documented.

By the Sun JNI example, I began by creating a Java class (C_Gcd.java) and writing the method declaration for the native method called gcd. Finally I added a static code block to the class that loads the library with System.loadLibrary, which I passed the library name (gcd) as an argument.

Next I generated the C headers (C_Gcd.h) from my Java source file using javah. After that, I created a .cpp file (I decided to use C++) in which the implementation of the native method would be. Next I wrote the actual gcd implementation.

Okay, that was easy. Next we compile the C++ code and make a shared library out of it. Here is what went wrong: I forgot that all dynamic library names are prefixed with 'lib'. I created a file called gcd.so.

Now, thinking everything was going fine, I wrote another Java class that would be using the native gcd method of the C_Gcd class. Now, I noticed that I want the gcd method to be static to avoid instatiating a C_Gcd object. So I happily went and added the static keyword to the gcd method declaration in the Java source file. Here was the second mistake: I forgot that changing the Java method declaration may, and probably will, lead to changes in the output of javah.

Now, believing it should run fine, I compiled everything again and run the Java application but got:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no gcd in java.library.path
Google reminded me of LD_LIBRARY_PATH, but setting it to the current path did not help. I kept having the same error.

Next I went to investigate the system with ldconfig. At some point I realised that not only is it a convention that all libraries are prefixed with lib, they must be! Unfortunately I went and changed the argument of System.loadLibrary to libgcd, which is of course wrong. So here was the third mistake: we refer to a library libfoo with mere foo, but I expressed, using the same example, liblibfoo. I still got the very same error.

After a moment I corrected the lattest glitch, and now, the error message changed:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Gcd.gcd(II)I
It was still an UnsatisfiedLinkError, but it dropped the talk about java.library.path.

Next, feeling desparete, I decided to take a look on the Java bytecode using javap. It looked completely healthy. I decided to take a break.

After the break, I opened the C++ sources again and began reading with no prejudice -- and there it was -- I finally saw that the C function declaration and its definition had been in conflict all the time. Corrected it, recompiled, and, uh, it finally worked!

And here we summarize on how to get JNI working painlessly under Linux:
  • Make sure to have LD_LIBRARY_PATH point to the library path. (*)
  • Make sure to name the library libFoo.so.
  • Make sure to refer to the library with mere Foo.
  • Be careful when changing the native method signatures.
(*) or launch the JVM with: java -Djava.library.path=. YourApplication in case your library is in the current directory.

For completeness, here's the source (with class names cleaned a bit):

== Gcd.java ==


public class Gcd {
public static native int gcd(int a, int b) throws IllegalArgumentException;
static {
System.loadLibrary("Gcd");
}
}



== Gcd.h ==


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Gcd */

#ifndef _Included_Gcd
#define _Included_Gcd
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Gcd
* Method: gcd
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_Gcd_gcd
(JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif



== Gcd.cpp ==


#include "Gcd.h"

JNIEXPORT jint JNICALL
Java_Gcd_gcd(JNIEnv *env, jclass cl, jint a, jint b)
{
if (a < 0 || b < 0) {
jclass ex = env->FindClass(
"java/lang/IllegalArgumentException");
env->ThrowNew(ex, "Both arguments must be positive.");
env->DeleteLocalRef(ex);
return -1;
}
while (a != b) {
if (a > b) {
a -= b;
} else {
b -= a;
}
}
return a;
}



== Test.java ==


public class Test {
public static void main(String[] args) {
System.out.println("gcd(12,24) = " + Gcd.gcd(12, 24));
}
}



== Makefile ==


CC=g++
INCLUDE=-I/usr/lib/jvm/java-1.5.0-sun-1.5.0.16/include/linux/ -I/usr/lib/jvm/java-1.5.0-sun-1.5.0.16/include

libGcd.so: Gcd.o
$(CC) -shared -o libGcd.so Gcd.o

Gcd.o: Gcd.cpp
$(CC) $(INCLUDE) -O3 -c Gcd.cpp

clean:
rm -f libGcd.so Gcd.o