Skip to content

Instantly share code, notes, and snippets.

@slingamn
Created March 28, 2012 04:10
Show Gist options
  • Save slingamn/2223536 to your computer and use it in GitHub Desktop.
Save slingamn/2223536 to your computer and use it in GitHub Desktop.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cbb1d4b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+build
+*.class
+*.swp
diff --git a/clean b/clean
new file mode 100755
index 0000000..8b346ec
--- /dev/null
+++ b/clean
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+rm *.h *.class *.o *.so
diff --git a/common-build.xml b/common-build.xml
index e202576..dcd2ff7 100644
--- a/common-build.xml
+++ b/common-build.xml
@@ -58,10 +58,10 @@
<property name="javac.deprecation" value="off"/>
<property name="javac.debug" value="on"/>
- <property name="javac.source" value="1.4"/>
- <property name="javac.target" value="1.4"/>
+ <property name="javac.source" value="1.6"/>
+ <property name="javac.target" value="1.6"/>
- <property name="javadoc.link" value="http://java.sun.com/j2se/1.4/docs/api/"/>
+ <property name="javadoc.link" value="http://java.sun.com/j2se/1.6/docs/api/"/>
<property name="javadoc.access" value="protected"/>
<property name="javadoc.charset" value="utf-8"/>
<property name="javadoc.dir" value="${common.dir}/build/docs/api"/>
diff --git a/shortbuild b/shortbuild
new file mode 100755
index 0000000..7c7968f
--- /dev/null
+++ b/shortbuild
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+#javac SystemCallException.java
+DIR="src/java/org/apache/lucene/store"
+
+ant
+
+CLASSPATH=build/lucene-core-2.9.4-dev.jar javah org.apache.lucene.store.JNIMman
+gcc -fPIC -g -c -Wall -I/usr/lib/jvm/java/include -I/usr/lib/jvm/java/include/linux ${DIR}/JNIMman.c
+gcc -shared -W1,-soname,jnimman.so.1 -o libjnimman.so JNIMman.o -lc
diff --git a/src/java/org/apache/lucene/store/JNILinuxConstants.java b/src/java/org/apache/lucene/store/JNILinuxConstants.java
new file mode 100644
index 0000000..16d9096
--- /dev/null
+++ b/src/java/org/apache/lucene/store/JNILinuxConstants.java
@@ -0,0 +1,27 @@
+package org.apache.lucene.store;
+
+class JNILinuxConstants {
+
+ /* TODO replace this, obviously */
+
+ public static final int O_RDONLY = 0;
+ public static final int O_WRONLY = 1;
+ public static final int O_RDWR = 2;
+
+ // protection settings for mmap
+ public static final int PROT_NONE = 0;
+ public static final int PROT_READ = 1;
+ public static final int PROT_WRITE = 2;
+ public static final int PROT_EXEC = 4;
+
+ // map modes
+ public static final int MAP_SHARED = 1;
+ public static final int MAP_PRIVATE = 2;
+
+ // mlock and mlockall
+ public static final int MCL_CURRENT = 1;
+ public static final int MCL_FUTURE = 2;
+
+ // sysconf values
+ public static final int _SC_PAGESIZE = 30;
+}
diff --git a/src/java/org/apache/lucene/store/JNIMman.c b/src/java/org/apache/lucene/store/JNIMman.c
new file mode 100644
index 0000000..5087755
--- /dev/null
+++ b/src/java/org/apache/lucene/store/JNIMman.c
@@ -0,0 +1,144 @@
+#include <jni.h>
+
+#include <inttypes.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+void Java_org_apache_lucene_store_JNIMman_throw_exception(JNIEnv *env, int saved_errno) {
+ jclass new_exc_cls;
+ jmethodID cid;
+ /* TODO proper packaging */
+ new_exc_cls = (*env)->FindClass(env, "SystemCallException");
+ // printf("found cls %p\n", new_exc_cls);
+ cid = (*env)->GetMethodID(env, new_exc_cls, "<init>", "(I)V");
+ //printf("found cons %p\n", cid);
+ jobject exc = (*env)->NewObject(env, new_exc_cls, cid, (jint) saved_errno);
+ (*env)->Throw(env, exc);
+}
+
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_mlock
+ (JNIEnv *env, jclass cls, jlong addr, jlong len) {
+ /* len is an actual signed Java long */
+ size_t native_len = (size_t) ((long) len);
+ int result = mlock((void *) addr, native_len);
+ if (result == -1) {
+ Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ }
+ return (jint) result;
+ }
+
+/*
+ * Class: JNIMman
+ * Method: munlock
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_munlock
+ (JNIEnv *env, jclass cls, jlong addr, jlong len) {
+ /* len is an actual signed Java long */
+ size_t native_len = (size_t) ((long) len);
+ int result = munlock((void *) addr, native_len);
+ if (result == -1) {
+ Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ }
+ return (jint) result;
+ }
+
+/*
+ * Class: JNIMman
+ * Method: mlockall
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_mlockall
+ (JNIEnv *env, jclass cls, jint flags) {
+ int result = mlockall((int) flags);
+ if (result == -1) {
+ Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ }
+ return (jint) result;
+ }
+
+/*
+ * Class: JNIMman
+ * Method: munlockall
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_munlockall
+ (JNIEnv *env, jclass cls) {
+ int result = munlockall();
+ if (result == -1) {
+ Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ }
+ return (jint) result;
+ }
+
+JNIEXPORT jlong JNICALL Java_org_apache_lucene_store_JNIMman_mmap
+ (JNIEnv *env, jclass cls, jlong addr, jlong length, jint prot, jint flags,
+ jint fd, jlong offset) {
+ void *result = mmap((void *) addr, (size_t) length, (int) prot,
+ (int) flags, (int) fd, (off_t) offset);
+ if (result == MAP_FAILED) {
+ Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ }
+ /* TODO is conversion right here? i just want to stuff the eight bytes */
+ return (jlong) result;
+ }
+
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_munmap
+ (JNIEnv * env, jclass cls, jlong addr, jlong length) {
+ int result = munmap((void *) addr, (size_t) length);
+ if (result == -1) Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ return (jint) result;
+ }
+
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_open__Ljava_lang_String_2II
+ (JNIEnv * env, jclass cls, jstring path, jint flags, jint mode) {
+ jboolean iscopy;
+ int fd;
+
+ const char *mfile = (*env)->GetStringUTFChars(env, path, &iscopy);
+ fd = open(mfile, (int) flags, (int) mode);
+ if (fd == -1) Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ (*env)->ReleaseStringUTFChars(env, path, mfile);
+ return (jint) fd;
+ }
+
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_open__Ljava_lang_String_2I
+ (JNIEnv * env, jclass cls, jstring path, jint flags) {
+ /* TODO is this actually a "default" mode argument :-| */
+ return Java_org_apache_lucene_store_JNIMman_open__Ljava_lang_String_2II(env, cls, path, flags, 0777);
+ }
+
+JNIEXPORT jint JNICALL Java_org_apache_lucene_store_JNIMman_close
+ (JNIEnv * env, jclass cls, jint fd) {
+ int result = close((int) fd);
+ if (result == -1) Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ return (jint) result;
+ }
+
+JNIEXPORT jlong JNICALL Java_org_apache_lucene_store_JNIMman_sysconf
+ (JNIEnv *env, jclass cls, jint name) {
+ long result = sysconf((int) name);
+ if (result == -1) Java_org_apache_lucene_store_JNIMman_throw_exception(env, errno);
+ return result;
+ }
+
+JNIEXPORT jbyte JNICALL Java_org_apache_lucene_store_JNIMman_getByte
+ (JNIEnv *env, jclass cls, jlong addr, jlong offset) {
+ /* don't perform pointer arithmetic in Javaspace;
+ wraparound overflow would be correct but the Java
+ behavior is to truncate. */
+ jbyte *byte_addr = (jbyte *) addr + offset;
+ return *byte_addr;
+ }
+
+JNIEXPORT void JNICALL Java_org_apache_lucene_store_JNIMman_getBytes
+ (JNIEnv *env, jclass cls, jbyteArray buf, jint offset, jint len,
+ jlong addr, jlong addr_offset) {
+ /* see above caution about pointer arithmetic in Javaspace */
+ jbyte *byte_addr = (jbyte *) addr + addr_offset;
+ /* may set an ArrayIndexOutOfBoundsException */
+ (*env)->SetByteArrayRegion(env, buf, offset, len, byte_addr);
+ }
diff --git a/src/java/org/apache/lucene/store/JNIMman.java b/src/java/org/apache/lucene/store/JNIMman.java
new file mode 100644
index 0000000..0094cd0
--- /dev/null
+++ b/src/java/org/apache/lucene/store/JNIMman.java
@@ -0,0 +1,57 @@
+package org.apache.lucene.store;
+
+/**
+ * JNI interface to Linux's sys/mman.h.
+ *
+ * All of this stuff is POSIX but no promises re. portability.
+ *
+ * @author [email protected]
+ *
+ */
+
+class JNIMman {
+
+ public static final long NULL = 0;
+
+ static {
+ System.loadLibrary("jnimman");
+ }
+
+ // int mlock(const void *addr, size_t len);
+ public native static int mlock(long addr, long len);
+
+ // int munlock(const void *addr, size_t len);
+ public native static int munlock(long addr, long len);
+
+ // int mlockall(int flags);
+ public native static int mlockall(int flags);
+
+ // int munlockall(void);
+ public native static int munlockall();
+
+ // void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
+ public native static long mmap(long addr, long length, int prot, int flags, int fd, long offset);
+
+ // int munmap(void *addr, size_t length);
+ public native static int munmap(long addr, long length);
+
+ // int open(const char *path, int flags);
+ public native static int open(String path, int flags);
+ // int open(const char *path, int flags, mode_t mode);
+ public native static int open(String path, int flags, int mode);
+ // int close(int fd);
+ public native static int close(int fd);
+
+ // long sysconf(int name);
+ public native static long sysconf(int name);
+
+ // direct access to the address space
+ public static native byte getByte(long addr, long offset);
+ public static native void getBytes(byte[] buffer, int offset, int len, long addr, long addrOffset);
+
+ public static void main(String args[]) {
+ System.out.println("Hello world");
+ int result = JNIMman.mlockall(3);
+ System.out.println("mlockall returned: " + result);
+ }
+}
diff --git a/src/java/org/apache/lucene/store/MLockDirectory.java b/src/java/org/apache/lucene/store/MLockDirectory.java
new file mode 100644
index 0000000..ca0cd86
--- /dev/null
+++ b/src/java/org/apache/lucene/store/MLockDirectory.java
@@ -0,0 +1,99 @@
+package org.apache.lucene.store;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class MLockDirectory extends FSDirectory {
+
+ private HashMap<String, MMapPlaceholder> fileMap = new HashMap<String, MMapPlaceholder>();
+ private long timeCreated;
+
+ private boolean mlock;
+
+ File path;
+
+ public MLockDirectory(File path, boolean vmtouchFiles, boolean mlock) throws IOException {
+ super(path, new NativeFSLockFactory());
+ this.path = path;
+ this.mlock = mlock;
+ this.timeCreated = System.currentTimeMillis();
+
+ for (String filename : this.listAll()) {
+ MMapPlaceholder placeholder = new MMapPlaceholder(filename);
+ if (vmtouchFiles) {
+ placeholder.vmtouchFile();
+ }
+ fileMap.put(filename, placeholder);
+ }
+
+ if (mlock) {
+ JNIMman.mlockall(JNILinuxConstants.MCL_CURRENT);
+ }
+ }
+
+ public void close() {
+ if (mlock) {
+ JNIMman.munlockall();
+ }
+
+ // just close all the underlying mmaps
+ for (MMapPlaceholder p : fileMap.values()) {
+ p.close();
+ }
+ }
+
+ // TODO consider a BufferedIndexInput?
+ // depends on the cost of the JNI call boundary
+ static class MLockIndexInput extends IndexInput {
+ MMapPlaceholder placeholder;
+ long filePointer = 0;
+
+ protected MLockIndexInput(MMapPlaceholder placeholder) {
+ super();
+ this.placeholder = placeholder;
+ }
+
+ public void close() {
+ // no-op
+ }
+
+ public long getFilePointer() {
+ return this.filePointer;
+ }
+
+ public void seek(long pos) {
+ this.filePointer = pos;
+ }
+
+ public long length() {
+ return this.placeholder.getLength();
+ }
+
+ public byte readByte() {
+ return this.placeholder.readByte(this.filePointer);
+ }
+
+ public void readBytes(byte[] b, int offset, int len) {
+ this.placeholder.readBytes(b, offset, len, this.filePointer);
+ }
+
+ }
+
+ public IndexInput openInput(String name) throws IOException {
+ MMapPlaceholder placeholder = this.fileMap.get(name);
+ if (placeholder == null) {
+ throw new IOException("No file named " + name);
+ }
+ return new MLockIndexInput(placeholder);
+ }
+
+ public IndexOutput createOutput(String name) {
+ return null;
+ }
+
+ public final long fileLength(String name) {
+ return this.fileMap.get(name).getLength();
+ }
+
+}
diff --git a/src/java/org/apache/lucene/store/MMapPlaceholder.java b/src/java/org/apache/lucene/store/MMapPlaceholder.java
new file mode 100644
index 0000000..ec1c20f
--- /dev/null
+++ b/src/java/org/apache/lucene/store/MMapPlaceholder.java
@@ -0,0 +1,84 @@
+package org.apache.lucene.store;
+
+import java.io.File;
+import java.io.IOException;
+
+class MMapPlaceholder {
+
+ File file;
+ String fileName;
+
+ int fd;
+ long addr;
+ long length;
+
+ boolean writeable = false;
+
+ public static final long pagesize = JNIMman.sysconf(JNILinuxConstants._SC_PAGESIZE);
+
+ public MMapPlaceholder(String name) {
+ this(name, false, false);
+ }
+
+ public MMapPlaceholder(File file) {
+ this(file.getName(), false, false);
+ }
+
+ public MMapPlaceholder(String name, boolean private_map, boolean writeable_map) {
+ /* stat the file; don't bother going through the actual stat(2) */
+ this.fileName = name;
+ this.file = new File(name);
+ this.length = file.length();
+
+ int mode = JNILinuxConstants.MAP_SHARED;
+ if (private_map) {
+ mode = JNILinuxConstants.MAP_PRIVATE;
+ }
+
+ int prot = JNILinuxConstants.PROT_READ;
+ if (writeable_map) {
+ this.writeable = writeable;
+ prot |= JNILinuxConstants.PROT_WRITE;
+ }
+
+ this.fd = JNIMman.open(name, JNILinuxConstants.O_RDONLY, 0);
+ this.addr = JNIMman.mmap(0, this.length, prot, mode, this.fd, 0);
+ }
+
+ public void close() {
+ JNIMman.munmap(this.addr, this.fd);
+ JNIMman.close(this.fd);
+ }
+
+ public long getLength() {
+ return this.length;
+ }
+
+ public byte readByte(long offset) {
+ return JNIMman.getByte(this.addr, offset);
+ }
+
+ public void readBytes(byte[] b, int bufferOffset, int length, long fileOffset) {
+ JNIMman.getBytes(b, bufferOffset, length, this.addr, fileOffset);
+ }
+
+ public void vmtouchFile() {
+ // read a byte from every page, as per hoyte's vmtouch
+ long pages_in_file = (length + pagesize - 1) / pagesize;
+ for (long pos = 0; pos < this.length; pos += this.pagesize) {
+ readByte(pos);
+ }
+ }
+
+ public static void main(String args[]) {
+ MMapPlaceholder p = new MMapPlaceholder("/etc/passwd");
+ byte[] b = new byte[128];
+ char c = (char) p.readByte(0);
+ System.out.println("Read the 0th byte: " + c);
+ p.readBytes(b, 0, 27, 21);
+ System.out.println("Read bytes 21-40: " + new String(b));
+ p.vmtouchFile();
+ System.out.println("vmtouch OK.");
+ }
+
+}
diff --git a/src/java/org/apache/lucene/store/SystemCallException.java b/src/java/org/apache/lucene/store/SystemCallException.java
new file mode 100644
index 0000000..9427142
--- /dev/null
+++ b/src/java/org/apache/lucene/store/SystemCallException.java
@@ -0,0 +1,18 @@
+import java.lang.Exception;
+
+class SystemCallException extends Exception {
+
+ int errno = -1;
+
+ public SystemCallException() {
+ super("SystemCallException: errno=?");
+ }
+
+ public SystemCallException(int errno) {
+ super("SystemCallException: errno=" + errno);
+ }
+
+ public int getErrno() {
+ return this.errno;
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment