JNI

Overview

This section describes how Jikes RVM interfaces to native code. There are three major aspects of this support:

JNI Functions

All of the 1.1 through 1.4 JNIEnv interface functions are implemented.

The functions are defined in the class JNIFunctions. Methods of this class are compiled with special prologues/epilogues that translate from native calling conventions to Java calling conventions and handle other details of the transition related to m-to-n threading. Currently the optimizing compiler does not support these specialized prologue/epilogue sequences so all methods in this class are baseline compiled. The prologue/epilogue sequences are actually generated by the platform-specific JNICompiler.

Invoking Native Methods

There are two mechanisms whereby RVM may transition from Java code to native code.

The first mechanism is when RVM calls a method of the class SysCall. The native methods thus invoked are defined in one of the C and C++ files of the JikesRVM executable. These native methods are non-blocking system calls or C library services. To implement a syscall, the RVM compilers generate a call sequence consistent with the platform's underlying calling convention. A syscall is not a GC-safe point, so syscalls may modify the Java heap (eg. memcpy()). For more details on the mechanics of adding a new syscall to the system, see the header comments of SysCall.java. Note again that the syscall methods are NOT JNI methods, but an independent (more efficient) interface that is specific to Jikes RVM.

The second mechanism is JNI. Naturally, the user writes JNI code using the JNI interface. RVM implements a call to a native method by using the platform-specific JNICompiler to generate a stub routine that manages the transition between Java bytecode and native code. A JNI call is a GC-safe point, since JNI code cannot freely modify the Java heap.

Interactions with m-to-n Threading

See the Thread Management subsection for more details on the thread system and m-to-n threading in Jikes RVM.

There are two ways to execute native code: syscalls and JNI. A Java thread that calls native code by either mechanism will never be preempted by Jikes RVM. As far as Jikes RVM is concerned, a Java thread that enters native code has exclusive access to the underlying Processor (pthread) until it returns to Java. Of course the OS may preempt the underlying pthread; this falls beyond Jikes RVM's control.

Some activities (eg. GC) require all threads currently running Java code to halt. So what happens when one Java thread forces a GC while another Java thread is executing native code?

If the native code is a syscall, then the VM stalls until the native code returns. Thus, all syscalls should be non-blocking operations that return fairly soon. Note that a syscall is not a GC-safe point.

On Linux/x86, Jikes RVM "hijacks" certain blocking system calls and reflects them back into the VM. The VM then uses nonblocking equivalents. This handles many of the common cases of blocking native code without requiring the full complexity of the timer-based preemption mechanism that we used to use on AIX. A complete solution would consist of implementing both mechanisms on both platforms. We hope to do this in the future.

We got GNU Classpath's (JNI-based) AWT support to work by adding code to Classpath that tells GTk (the windowing toolkit Classpath uses) to use Jikes RVM's Java threading primitives instead of the pthread-based ones that it uses by default. Jikes RVM automatically tells Classpath to do this by setting the Java system property gnu.classpath.awt.gtk.portable.native.sync at boot time. If your native code uses the glib threading primitives, as GTk does, then this will work for you, too.

Implementation Details

Supporting the combination of blocking native code and m-to-n threading is inherently complicated. Unfortunately the Jikes RVM implementation is further complicated by the fact that too much of the control logic for transitions between C and Java code is embedded in the low-level, platform-specific JNICompiler classes. As a result, the code is hard to maintain and the JNI implementations on different platforms tend to diverge.

We have some ideas for a redesign that would enable more of the control logic to be embodied in shared Java code, but there are a few minor issues to be worked out. Hopefully this will happen eventually.

Missing Features

Things JNI Can't Handle

Contributions of any of the missing functionality described here (and associated tests) would be greatly appreciated.