Permalink
Browse files

Reduce the amount of JNI code

The methods which manipulates integer port name and message buffers directly
are marked with "throws Unsafe" to prevent them from being used advertently.
Methods which encapsulate the low-level code and expose only type-safe
abstractions to the user catch the Unsafe pseudo-exception.

This is similar in principle to the "semantic regimes" of org.vmmagic.

Unfortunately using a pseudo-exception in this way introduces some quirks,
as we have to silence compiler warning and "handle" the exception which is
never actually thrown. Ideally this would be done with annotations instead
(@Unsafe, @AssertSafe) and checked by a custom annotation processor.

A third option would be to simply make unsafe methods package-private.
Drawbacks are disallowing users access to these methods (even if they know
what they're doing), and the lack of check for encapsulation of low-level
abstractions.
  • Loading branch information...
jeremie-koenig committed Jul 6, 2011
1 parent 2b91fcf commit 993f1932ec8a9de1cef6ab7ab01b6093d985e63b
Showing with 198 additions and 141 deletions.
  1. +12 −3 HelloMach.java
  2. +6 −5 Makefile
  3. +3 −4 hurd/Hurd.c
  4. +15 −2 hurd/Hurd.java
  5. +0 −24 mach-java.h
  6. +31 −8 mach/Mach.c
  7. +45 −3 mach/Mach.java
  8. +20 −14 mach/MachMsg.java
  9. +0 −53 mach/MachPort.c
  10. +57 −25 mach/MachPort.java
  11. +9 −0 mach/Unsafe.java
View
@@ -1,14 +1,15 @@
import org.gnu.mach.Mach;
import org.gnu.mach.MachPort;
import org.gnu.mach.MachMsg;
+import org.gnu.mach.Unsafe;
import org.gnu.hurd.Hurd;
public class HelloMach {
private static void hello(MachPort stdout)
throws MachMsg.TypeCheckException
{
MachMsg msg = new MachMsg(1000);
- MachPort reply = Mach.replyPort();
+ MachPort reply = MachPort.allocateReplyPort();
/* mach_msg_header_t */
msg.setRemotePort(stdout, MachMsg.Type.COPY_SEND);
@@ -21,8 +22,16 @@ private static void hello(MachPort stdout)
/* Offset */
msg.putInteger64(-1);
- int err = Mach.msg(msg.buf, Mach.SEND_MSG | Mach.RCV_MSG, reply, Mach.MSG_TIMEOUT_NONE, null);
- System.out.println("err = " + err);
+ try {
+ int err = Mach.msg(msg.buf,
+ Mach.SEND_MSG | Mach.RCV_MSG,
+ reply.name(),
+ Mach.MSG_TIMEOUT_NONE,
+ MachPort.NULL.name());
+ reply.releaseName();
+ MachPort.NULL.releaseName();
+ System.out.println("err = " + err);
+ } catch(Unsafe e) {}
msg.buf.position(msg.buf.getInt(4)).flip().position(24);
int retcode = msg.getInteger32();
View
@@ -13,7 +13,8 @@ CLASSES = $(patsubst %.java,%.class,$(JAVASRCS))
# JNI shared library
JNILIB = libhurd-java.so
JNISRCS = $(shell find -name \*.c)
-JNIHDRS = $(patsubst %.c,%.h,$(JNISRCS))
+JNIHDRS = mach/Mach.h mach/Mach$$Port.h hurd/Hurd.h
+
JNIOBJS = $(patsubst %.c,%.o,$(JNISRCS))
all: test
@@ -26,9 +27,9 @@ doc: $(JAVASRCS)
$(RM) -r $@
mv $@.n $@
-%.h: %.class
- $(JAVAH) -cp . -o $@.n $*
- mv $@.n $@
+%.h: $(CLASSES)
+ $(JAVAH) -cp . -o '$@.n' '$*'
+ mv '$@.n' '$@'
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
@@ -41,7 +42,7 @@ test: $(CLASSES) $(JNILIB)
LD_LIBRARY_PATH=. java HelloMach
clean:
- $(RM) $(JNILIB) $(JNIOBJS) $(JNIHDRS)
+ $(RM) $(JNILIB) $(JNIOBJS) $(patsubst %,'%',$(JNIHDRS))
find -name \*.class | xargs $(RM)
$(JNIOBJS): $(JNIHDRS)
View
@@ -1,9 +1,8 @@
-#include <mach-java.h>
#include <hurd.h>
#include "Hurd.h"
-JNIEXPORT jobject JNICALL
-Java_org_gnu_hurd_Hurd_getdport(JNIEnv *env, jobject obj, jint fd)
+JNIEXPORT jint JNICALL
+Java_org_gnu_hurd_Hurd_unsafeGetdport(JNIEnv *env, jobject obj, jint fd)
{
- return mach_java_makeport(env, getdport(fd));
+ return getdport(fd);
}
View
@@ -1,13 +1,26 @@
package org.gnu.hurd;
import org.gnu.mach.MachPort;
+import org.gnu.mach.Unsafe;
/**
* Ambient authority of a Hurd process.
*/
public class Hurd {
/**
- * Retreive the MachPort for a given file descriptor.
+ * Return the io server port for file descriptor FD.
+ * This adds a Mach user reference to the returned port.
*/
- public native MachPort getdport(int fd);
+ private native int unsafeGetdport(int fd) throws Unsafe;
+
+ /**
+ * Return a MachPort object for file descriptor FD.
+ */
+ public MachPort getdport(int fd) {
+ try {
+ return new MachPort(unsafeGetdport(fd));
+ } catch(Unsafe e) {
+ return null;
+ }
+ }
};
View
@@ -1,24 +0,0 @@
-/**
- * @file mach-java.h
- * @brief JNI interfaces for the org.gnu.mach package.
- */
-#ifndef __MACH_JAVA_H__
-#define __MACH_JAVA_H__
-
-#include <mach.h>
-#include <jni.h>
-
-/**
- * Construct a MachPort object for the given mach_port_t.
- *
- * This consumes one reference to @p name. The reference will be released
- * when deallocate() is called, or when the new object is collected.
- */
-jobject mach_java_makeport(JNIEnv *env, mach_port_t name);
-
-/**
- * Retreive a port name from a MachPort object.
- */
-mach_port_t mach_java_getport(JNIEnv *env, jobject obj);
-
-#endif
View
@@ -1,17 +1,17 @@
#include <assert.h>
-#include <mach-java.h>
+#include <mach.h>
#include "Mach.h"
+#include "Mach$Port.h"
-JNIEXPORT jobject JNICALL
+JNIEXPORT jint JNICALL
Java_org_gnu_mach_Mach_replyPort (JNIEnv *env, jclass cls)
{
- /* FIXME: exception */
- return mach_java_makeport(env, mach_reply_port());
+ return mach_reply_port();
}
JNIEXPORT jint JNICALL
Java_org_gnu_mach_Mach_msg (JNIEnv *env, jclass cls, jobject msg, jint option,
- jobject rcvName, jlong timeout, jobject notify)
+ jint rcvName, jlong timeout, jint notify)
{
static jmethodID mid_position = NULL;
if(mid_position == NULL) {
@@ -24,7 +24,30 @@ Java_org_gnu_mach_Mach_msg (JNIEnv *env, jclass cls, jobject msg, jint option,
jint msgPos = (*env)->CallIntMethod(env, msg, mid_position);
assert(msgAddr); /* XXX exception. */
- return mach_msg(msgAddr, option, msgPos, msgSize,
- mach_java_getport(env, rcvName), timeout,
- mach_java_getport(env, notify));
+ return mach_msg(msgAddr, option, msgPos, msgSize, rcvName, timeout, notify);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_gnu_mach_Mach_taskSelf (JNIEnv *env, jclass cls)
+{
+ return mach_task_self();
+}
+
+JNIEXPORT jint JNICALL
+Java_org_gnu_mach_Mach_00024Port_allocate (JNIEnv *env, jclass cls, jint task, jint right)
+{
+ mach_port_t name;
+ kern_return_t err;
+
+ err = mach_port_allocate(task, right, &name);
+ assert(err == KERN_SUCCESS);
+
+ return name;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_gnu_mach_Mach_00024Port_deallocate (JNIEnv *env, jclass cls, jint task, jint name)
+{
+ return mach_port_deallocate(task, name);
}
+
View
@@ -38,7 +38,7 @@
* This is a wrapper around the mach_reply_port() system call, which
* creates a new MachPort object with the returned name.
*/
- public static native MachPort replyPort();
+ public static native int replyPort() throws Unsafe;
/**
* Native call to mach_msg().
@@ -53,7 +53,49 @@
*
* XXX repomper la doc du manuel.
*/
- public static native int msg(ByteBuffer msg, int option,
- MachPort rcvName, long timeout, MachPort notify);
+ public static native int
+ msg(ByteBuffer msg, int option, int rcvName, long timeout, int notify)
+ throws Unsafe;
+
+ /**
+ * This system call returns the calling thread's task port.
+ *
+ * mach_task_self has an effect equivalent to receiving a send right for
+ * the task port. mach_task_self returns the name of the send right. In
+ * particular, successive calls will increase the calling task's
+ * user-reference count for the send right.
+ *
+ * As a special exception, the kernel will overrun the user reference
+ * count of the task name port, so that this function can not fail for
+ * that reason. Because of this, the user should not deallocate the port
+ * right if an overrun might have happened. Otherwise the reference
+ * count could drop to zero and the send right be destroyed while the
+ * user still expects to be able to use it. As the kernel does not make
+ * use of the number of extant send rights anyway, this is safe to do
+ * (the task port itself is not destroyed, even when there are no send
+ * rights anymore).
+ *
+ * The function returns MACH_PORT_NULL if a resource shortage prevented
+ * the reception of the send right, MACH_PORT_NULL if the task port is
+ * currently null, MACH_PORT_DEAD if the task port is currently dead.
+ */
+ public static native int taskSelf() throws Unsafe;
+
+ /**
+ * Task operations on ports.
+ *
+ * These calls are actually RPCs rather than system calls and this
+ * functionality will eventually be replaced by MIG-generated stubs.
+ */
+ public static class Port {
+ public static final int RIGHT_SEND = 0;
+ public static final int RIGHT_RECEIVE = 1;
+ public static final int RIGHT_SEND_ONCE = 2;
+ public static final int RIGHT_PORT_SET = 3;
+ public static final int RIGHT_DEAD_NAME = 4;
+
+ public static native int allocate(int task, int right) throws Unsafe;
+ public static native int deallocate(int task, int name) throws Unsafe;
+ }
}
View
@@ -255,12 +255,14 @@ public ByteBuffer buf() {
*/
private void clearNames() {
/* Release references. */
- if(remotePort != null)
- remotePort.releaseName();
- if(localPort != null)
- localPort.releaseName();
- for(MachPort port : refPorts)
- port.releaseName();
+ try {
+ if(remotePort != null)
+ remotePort.releaseName();
+ if(localPort != null)
+ localPort.releaseName();
+ for(MachPort port : refPorts)
+ port.releaseName();
+ } catch(Unsafe e) {}
/* Forget them. */
remotePort = null;
@@ -296,11 +298,13 @@ private void putBits() {
/** Set the header's {@code msgh_remote_port} field. */
public synchronized MachMsg setRemotePort(MachPort port, Type type) {
- if(remotePort != null)
- remotePort.releaseName();
+ try {
+ if(remotePort != null)
+ remotePort.releaseName();
- remotePort = port;
- buf.putInt(8, remotePort.name());
+ remotePort = port;
+ buf.putInt(8, remotePort.name());
+ } catch(Unsafe e) {}
remoteType = type;
putBits();
@@ -310,11 +314,13 @@ public synchronized MachMsg setRemotePort(MachPort port, Type type) {
/** Set the header's {@code msgh_local_port} field. */
public synchronized MachMsg setLocalPort(MachPort port, Type type) {
- if(localPort != null)
- localPort.releaseName();
+ try {
+ if(localPort != null)
+ localPort.releaseName();
- localPort = port;
- buf.putInt(12, localPort.name());
+ localPort = port;
+ buf.putInt(12, localPort.name());
+ } catch(Unsafe e) {}
localType = type;
putBits();
View
@@ -1,53 +0,0 @@
-#include <assert.h>
-#include <mach.h>
-#include <mach-java.h>
-#include "MachPort.h"
-
-static jfieldID nameID;
-static jmethodID ctorID;
-static jclass cls_MachPort;
-
-JNIEXPORT void JNICALL
-Java_org_gnu_mach_MachPort_initIDs(JNIEnv *env, jclass cls)
-{
- cls_MachPort = cls;
- nameID = (*env)->GetFieldID(env, cls_MachPort, "name", "I");
- ctorID = (*env)->GetMethodID(env, cls_MachPort, "<init>", "(I)V");
- assert(nameID != NULL);
-}
-
-jobject mach_java_makeport(JNIEnv *env, mach_port_t name)
-{
- if(!cls_MachPort) {
- /* Ensures initIDs() has been called. */
- jclass cls = (*env)->FindClass(env, "org/gnu/mach/MachPort");
- (*env)->DeleteLocalRef(env, cls);
- }
- return (*env)->NewObject(env, cls_MachPort, ctorID, (jint) name);
-}
-
-mach_port_t mach_java_getport(JNIEnv *env, jobject obj)
-{
- return obj ? (*env)->GetIntField(env, obj, nameID) : MACH_PORT_NULL;
-}
-
-JNIEXPORT jobject JNICALL
-Java_org_gnu_mach_MachPort_allocate(JNIEnv *env, jclass cls)
-{
- mach_port_t port;
- kern_return_t err;
-
- err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
- if(err != KERN_SUCCESS) {
- /* FIXME: handle errors */
- }
-
- return mach_java_makeport(env, port);
-}
-
-JNIEXPORT void JNICALL
-Java_org_gnu_mach_MachPort_nativeDeallocate(JNIEnv *env, jobject obj)
-{
- mach_port_deallocate(mach_task_self(), mach_java_getport(env, obj));
-}
-
Oops, something went wrong.

0 comments on commit 993f193

Please sign in to comment.