From e0556b51c4223843c801b3cc6a10c13248cff8fd Mon Sep 17 00:00:00 2001 From: Casey Marshall Date: Wed, 31 May 2006 00:57:16 +0000 Subject: 2006-05-30 Casey Marshall Merge scatter/gather NIO patch by Michael Barker to branch. --- ChangeLog-jessie-nio | 4 + gnu/java/nio/PipeImpl.java | 32 +- gnu/java/nio/SelectorImpl.java | 2 + gnu/java/nio/SocketChannelImpl.java | 120 +++---- gnu/java/nio/channels/FileChannelImpl.java | 49 +-- include/Makefile.am | 3 + java/nio/channels/FileChannel.java | 14 +- native/jni/java-nio/Makefile.am | 1 + native/jni/java-nio/gnu_java_nio_VMChannel.c | 519 +++++++++++++++++++++++++++ vm/reference/gnu/java/nio/VMChannel.java | 197 ++++++++++ 10 files changed, 801 insertions(+), 140 deletions(-) create mode 100644 native/jni/java-nio/gnu_java_nio_VMChannel.c create mode 100644 vm/reference/gnu/java/nio/VMChannel.java diff --git a/ChangeLog-jessie-nio b/ChangeLog-jessie-nio index 82dc3c7f9..a325c4f1b 100644 --- a/ChangeLog-jessie-nio +++ b/ChangeLog-jessie-nio @@ -1,3 +1,7 @@ +2006-05-30 Casey Marshall + + Merge scatter/gather NIO patch by Michael Barker to branch. + 2006-03-28 Casey Marshall * jessie-tests/run-tests.sh: add new tests; count number of tests diff --git a/gnu/java/nio/PipeImpl.java b/gnu/java/nio/PipeImpl.java index f7b01c8b7..cccaa3988 100644 --- a/gnu/java/nio/PipeImpl.java +++ b/gnu/java/nio/PipeImpl.java @@ -37,6 +37,7 @@ exception statement from your version. */ package gnu.java.nio; + import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Pipe; @@ -47,12 +48,14 @@ class PipeImpl extends Pipe public static final class SourceChannelImpl extends Pipe.SourceChannel { private int native_fd; + private VMChannel vmch; public SourceChannelImpl (SelectorProvider selectorProvider, int native_fd) { super (selectorProvider); this.native_fd = native_fd; + vmch = VMChannel.getVMChannel(this); } protected final void implCloseSelectableChannel() @@ -64,19 +67,19 @@ class PipeImpl extends Pipe protected void implConfigureBlocking (boolean blocking) throws IOException { - throw new Error ("Not implemented"); + vmch.setBlocking(blocking); } public final int read (ByteBuffer src) throws IOException { - throw new Error ("Not implemented"); + return vmch.read(src); } public final long read (ByteBuffer[] srcs) throws IOException { - return read (srcs, 0, srcs.length); + return vmch.readScattering(srcs, 0, srcs.length); } public final synchronized long read (ByteBuffer[] srcs, int offset, @@ -89,13 +92,7 @@ class PipeImpl extends Pipe || len > srcs.length - offset) throw new IndexOutOfBoundsException(); - long bytesRead = 0; - - for (int index = 0; index < len; index++) - bytesRead += read (srcs [offset + index]); - - return bytesRead; - + return vmch.readScattering(srcs, offset, len); } public final int getNativeFD() @@ -107,12 +104,14 @@ class PipeImpl extends Pipe public static final class SinkChannelImpl extends Pipe.SinkChannel { private int native_fd; + private VMChannel vmch; public SinkChannelImpl (SelectorProvider selectorProvider, int native_fd) { super (selectorProvider); this.native_fd = native_fd; + vmch = VMChannel.getVMChannel(this); } protected final void implCloseSelectableChannel() @@ -124,19 +123,19 @@ class PipeImpl extends Pipe protected final void implConfigureBlocking (boolean blocking) throws IOException { - throw new Error ("Not implemented"); + vmch.setBlocking(blocking); } public final int write (ByteBuffer dst) throws IOException { - throw new Error ("Not implemented"); + return vmch.write(dst); } public final long write (ByteBuffer[] srcs) throws IOException { - return write (srcs, 0, srcs.length); + return vmch.writeGathering(srcs, 0, srcs.length); } public final synchronized long write (ByteBuffer[] srcs, int offset, int len) @@ -147,13 +146,8 @@ class PipeImpl extends Pipe || len < 0 || len > srcs.length - offset) throw new IndexOutOfBoundsException(); - - long bytesWritten = 0; - for (int index = 0; index < len; index++) - bytesWritten += write (srcs [offset + index]); - - return bytesWritten; + return vmch.writeGathering(srcs, offset, len); } public final int getNativeFD() diff --git a/gnu/java/nio/SelectorImpl.java b/gnu/java/nio/SelectorImpl.java index e10f71574..7639c9ae3 100644 --- a/gnu/java/nio/SelectorImpl.java +++ b/gnu/java/nio/SelectorImpl.java @@ -379,6 +379,8 @@ public class SelectorImpl extends AbstractSelector result = new DatagramChannelSelectionKey (ch, this); else if (ch instanceof ServerSocketChannelImpl) result = new ServerSocketChannelSelectionKey (ch, this); + else if (ch instanceof gnu.java.nio.SocketChannelImpl) + result = new gnu.java.nio.SocketChannelSelectionKeyImpl((gnu.java.nio.SocketChannelImpl)ch, this); else throw new InternalError ("No known channel type"); diff --git a/gnu/java/nio/SocketChannelImpl.java b/gnu/java/nio/SocketChannelImpl.java index 680eba2f9..697987eb6 100644 --- a/gnu/java/nio/SocketChannelImpl.java +++ b/gnu/java/nio/SocketChannelImpl.java @@ -41,8 +41,6 @@ package gnu.java.nio; import gnu.java.net.PlainSocketImpl; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; @@ -110,7 +108,8 @@ public final class SocketChannelImpl extends SocketChannel protected void implConfigureBlocking (boolean blocking) throws IOException { - socket.setSoTimeout (blocking ? 0 : NIOConstants.DEFAULT_TIMEOUT); + VMChannel vmch = VMChannel.getVMChannel(impl); + vmch.setBlocking(blocking); } public boolean connect (SocketAddress remote) throws IOException @@ -215,25 +214,6 @@ public final class SocketChannelImpl extends SocketChannel { if (!isConnected()) throw new NotYetConnectedException(); - - byte[] data; - int offset = 0; - InputStream input = socket.getInputStream(); - int available = input.available(); - int len = dst.remaining(); - - if ((! isBlocking()) && available == 0) - return 0; - - if (dst.hasArray()) - { - offset = dst.arrayOffset() + dst.position(); - data = dst.array(); - } - else - { - data = new byte [len]; - } int readBytes = 0; boolean completed = false; @@ -241,8 +221,8 @@ public final class SocketChannelImpl extends SocketChannel try { begin(); - socket.getPlainSocketImpl().setInChannelOperation(true); - readBytes = input.read (data, offset, len); + VMChannel vmch = VMChannel.getVMChannel(impl); + readBytes = vmch.read(dst); completed = true; } finally @@ -251,15 +231,6 @@ public final class SocketChannelImpl extends SocketChannel socket.getPlainSocketImpl().setInChannelOperation(false); } - if (readBytes > 0) - if (dst.hasArray()) - { - dst.position (dst.position() + readBytes); - } - else - { - dst.put (data, offset, readBytes); - } return readBytes; } @@ -270,16 +241,22 @@ public final class SocketChannelImpl extends SocketChannel if (!isConnected()) throw new NotYetConnectedException(); - if ((offset < 0) - || (offset > dsts.length) - || (length < 0) - || (length > (dsts.length - offset))) - throw new IndexOutOfBoundsException(); - long readBytes = 0; + + boolean completed = false; - for (int index = offset; index < length; index++) - readBytes += read (dsts [index]); + try + { + begin(); + VMChannel vmch = VMChannel.getVMChannel(impl); + readBytes = vmch.readScattering(dsts, offset, length); + completed = true; + } + finally + { + end (completed); + socket.getPlainSocketImpl().setInChannelOperation(false); + } return readBytes; } @@ -289,30 +266,15 @@ public final class SocketChannelImpl extends SocketChannel { if (!isConnected()) throw new NotYetConnectedException(); - - byte[] data; - int offset = 0; - int len = src.remaining(); - - if (!src.hasArray()) - { - data = new byte [len]; - src.get (data, 0, len); - } - else - { - offset = src.arrayOffset() + src.position(); - data = src.array(); - } - OutputStream output = socket.getOutputStream(); + int readBytes = 0; boolean completed = false; try { begin(); - socket.getPlainSocketImpl().setInChannelOperation(true); - output.write (data, offset, len); + VMChannel vmch = VMChannel.getVMChannel(impl); + readBytes = vmch.write(src); completed = true; } finally @@ -321,12 +283,8 @@ public final class SocketChannelImpl extends SocketChannel socket.getPlainSocketImpl().setInChannelOperation(false); } - if (src.hasArray()) - { - src.position (src.position() + len); - } - - return len; + + return readBytes; } public long write (ByteBuffer[] srcs, int offset, int length) @@ -334,18 +292,24 @@ public final class SocketChannelImpl extends SocketChannel { if (!isConnected()) throw new NotYetConnectedException(); - - if ((offset < 0) - || (offset > srcs.length) - || (length < 0) - || (length > (srcs.length - offset))) - throw new IndexOutOfBoundsException(); - - long writtenBytes = 0; - - for (int index = offset; index < length; index++) - writtenBytes += write (srcs [index]); - - return writtenBytes; + + long readBytes = 0; + boolean completed = false; + + try + { + begin(); + VMChannel vmch = VMChannel.getVMChannel(impl); + readBytes = vmch.writeGathering(srcs, offset, length); + completed = true; + } + finally + { + end (completed); + socket.getPlainSocketImpl().setInChannelOperation(false); + } + + + return readBytes; } } diff --git a/gnu/java/nio/channels/FileChannelImpl.java b/gnu/java/nio/channels/FileChannelImpl.java index 671ae5bb1..ed439e141 100644 --- a/gnu/java/nio/channels/FileChannelImpl.java +++ b/gnu/java/nio/channels/FileChannelImpl.java @@ -40,6 +40,7 @@ package gnu.java.nio.channels; import gnu.classpath.Configuration; import gnu.java.nio.FileLockImpl; +import gnu.java.nio.VMChannel; import java.io.File; import java.io.FileNotFoundException; @@ -102,6 +103,7 @@ public final class FileChannelImpl extends FileChannel // we want to make sure this has the value -1. This is the most // efficient way to accomplish that. private int fd = -1; + private VMChannel ch; private int mode; @@ -123,6 +125,7 @@ public final class FileChannelImpl extends FileChannel description = path; fd = open (path, mode); this.mode = mode; + this.ch = VMChannel.getVMChannel(this); // First open the file and then check if it is a a directory // to avoid race condition. @@ -155,6 +158,7 @@ public final class FileChannelImpl extends FileChannel this.fd = fd; this.mode = mode; this.description = "descriptor(" + fd + ")"; + this.ch = VMChannel.getVMChannel(this); } private native int open (String path, int mode) throws FileNotFoundException; @@ -181,6 +185,7 @@ public final class FileChannelImpl extends FileChannel public int read (ByteBuffer dst) throws IOException { + /* int result; byte[] buffer = new byte [dst.remaining ()]; @@ -190,6 +195,8 @@ public final class FileChannelImpl extends FileChannel dst.put (buffer, 0, result); return result; + */ + return ch.read(dst); } public int read (ByteBuffer dst, long position) @@ -214,33 +221,12 @@ public final class FileChannelImpl extends FileChannel public long read (ByteBuffer[] dsts, int offset, int length) throws IOException { - long result = 0; - - for (int i = offset; i < offset + length; i++) - { - result += read (dsts [i]); - } - - return result; + return ch.readScattering(dsts, offset, length); } public int write (ByteBuffer src) throws IOException { - int len = src.remaining (); - if (src.hasArray()) - { - byte[] buffer = src.array(); - write(buffer, src.arrayOffset() + src.position(), len); - src.position(src.position() + len); - } - else - { - // Use a more efficient native method! FIXME! - byte[] buffer = new byte [len]; - src.get (buffer, 0, len); - write (buffer, 0, len); - } - return len; + return ch.write(src); } public int write (ByteBuffer src, long position) @@ -274,14 +260,7 @@ public final class FileChannelImpl extends FileChannel public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { - long result = 0; - - for (int i = offset;i < offset + length;i++) - { - result += write (srcs[i]); - } - - return result; + return ch.writeGathering(srcs, offset, length); } public native MappedByteBuffer mapImpl (char mode, long position, int size) @@ -563,4 +542,12 @@ public final class FileChannelImpl extends FileChannel + ",mode=" + mode + "," + description + "]"); } + + /** + * @return The native file descriptor. + */ + public int getNativeFD() + { + return fd; + } } diff --git a/include/Makefile.am b/include/Makefile.am index 0517e125a..160a5069a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -119,6 +119,7 @@ $(GTKPEER_H_FILES) \ $(QTPEER_H_FILES) \ $(top_srcdir)/include/gnu_java_net_PlainDatagramSocketImpl.h \ $(top_srcdir)/include/gnu_java_net_PlainSocketImpl.h \ +$(top_srcdir)/include/gnu_java_nio_VMChannel.h \ $(top_srcdir)/include/gnu_java_nio_VMPipe.h \ $(top_srcdir)/include/gnu_java_nio_VMSelector.h \ $(top_srcdir)/include/gnu_java_nio_channels_FileChannelImpl.h \ @@ -168,6 +169,8 @@ $(top_srcdir)/include/gnu_java_net_PlainDatagramSocketImpl.h: $(top_srcdir)/gnu/ $(JAVAH) -o $@ gnu.java.net.PlainDatagramSocketImpl $(top_srcdir)/include/gnu_java_net_PlainSocketImpl.h: $(top_srcdir)/gnu/java/net/PlainSocketImpl.java $(JAVAH) -o $@ gnu.java.net.PlainSocketImpl +$(top_srcdir)/include/gnu_java_nio_VMChannel.h: $(top_srcdir)/vm/reference/gnu/java/nio/VMChannel.java + $(JAVAH) -o $@ gnu.java.nio.VMChannel $(top_srcdir)/include/gnu_java_nio_VMPipe.h: $(top_srcdir)/vm/reference/gnu/java/nio/VMPipe.java $(JAVAH) -o $@ gnu.java.nio.VMPipe $(top_srcdir)/include/gnu_java_nio_VMSelector.h: $(top_srcdir)/vm/reference/gnu/java/nio/VMSelector.java diff --git a/java/nio/channels/FileChannel.java b/java/nio/channels/FileChannel.java index 0eefffbe9..3aa199909 100644 --- a/java/nio/channels/FileChannel.java +++ b/java/nio/channels/FileChannel.java @@ -114,12 +114,7 @@ public abstract class FileChannel extends AbstractInterruptibleChannel */ public final long write(ByteBuffer[] srcs) throws IOException { - long result = 0; - - for (int i = 0; i < srcs.length; i++) - result += write(srcs[i]); - - return result; + return write(srcs, 0, srcs.length); } /** @@ -169,12 +164,7 @@ public abstract class FileChannel extends AbstractInterruptibleChannel */ public final long read(ByteBuffer[] dsts) throws IOException { - long result = 0; - - for (int i = 0; i < dsts.length; i++) - read(dsts[i]); - - return result; + return read(dsts, 0, dsts.length); } /** diff --git a/native/jni/java-nio/Makefile.am b/native/jni/java-nio/Makefile.am index 8fd5ba3ae..ea9562389 100644 --- a/native/jni/java-nio/Makefile.am +++ b/native/jni/java-nio/Makefile.am @@ -1,6 +1,7 @@ nativelib_LTLIBRARIES = libjavanio.la libjavanio_la_SOURCES = gnu_java_nio_VMPipe.c \ + gnu_java_nio_VMChannel.c \ gnu_java_nio_VMSelector.c \ gnu_java_nio_channels_FileChannelImpl.c \ gnu_java_nio_charset_iconv_IconvDecoder.c \ diff --git a/native/jni/java-nio/gnu_java_nio_VMChannel.c b/native/jni/java-nio/gnu_java_nio_VMChannel.c new file mode 100644 index 000000000..b2551e55e --- /dev/null +++ b/native/jni/java-nio/gnu_java_nio_VMChannel.c @@ -0,0 +1,519 @@ +/* gnu_java_nio_VMChannel.c - + Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gnu_java_nio_VMChannel.h" + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#define IO_EXCEPTION "java/io/IOException" +#define NON_READABLE_CHANNEL_EXCEPTION "java/nio/channels/NonReadableChannelException" +#define NON_WRITABLE_CHANNEL_EXCEPTION "java/nio/channels/NonWritableChannelException" + +/* + * Limit to maximum of 16 buffers + */ +#define JCL_IOV_MAX 16 + +#ifdef __cplusplus +extern "C" +{ +#endif + +static jfieldID address_fid; +static jmethodID get_position_mid; +static jmethodID set_position_mid; +static jmethodID get_limit_mid; +static jmethodID set_limit_mid; +static jmethodID has_array_mid; +static jmethodID array_mid; +static jmethodID array_offset_mid; + +jmethodID +get_method_id(JNIEnv *env, jclass clazz, const char *name, + const char *sig) +{ + jmethodID mid = (*env)->GetMethodID(env, clazz, name, sig); + if (mid == NULL) + { + JCL_ThrowException(env, "java/lang/InternalError", name); + return NULL; + } + + return mid; +} + +enum JCL_buffer_type { DIRECT, ARRAY, UNKNOWN }; + +struct JCL_buffer +{ + enum JCL_buffer_type type; + jbyte *ptr; + jint offset; + jint position; + jint limit; + jint count; +}; + +void +JCL_print_buffer(JNIEnv *env, struct JCL_buffer *buf) +{ + fprintf(stdout, "Buffer - type: %d, ptr: %d\n", buf->type, (int)buf->ptr); + fflush(stdout); +} + + +int +JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf) +{ + jobject address = (*env)->GetObjectField(env, bbuf, address_fid); + + buf->position = (*env)->CallIntMethod(env, bbuf, get_position_mid); + buf->limit = (*env)->CallIntMethod(env, bbuf, get_limit_mid); + buf->offset = 0; + buf->count = 0; + buf->type = UNKNOWN; + + if (address != NULL) + { + buf->ptr = (jbyte *) JCL_GetRawData(env, address); + buf->type = DIRECT; + (*env)->DeleteLocalRef(env, address); + } + else + { + jboolean has_array; + has_array = (*env)->CallBooleanMethod(env, bbuf, has_array_mid); + + if (has_array == JNI_TRUE) + { + jbyteArray arr; + buf->offset = (*env)->CallIntMethod(env, bbuf, array_offset_mid); + arr = (*env)->CallObjectMethod(env, bbuf, array_mid); + buf->ptr = (*env)->GetByteArrayElements(env, arr, 0); + buf->type = ARRAY; + (*env)->DeleteLocalRef(env, arr); + } + else + { + return -1; + } + } + + return 0; +} + +void +JCL_release_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, + jint action) +{ + jbyteArray arr; + + /* Set the position to the appropriate value */ + if (buf->count > 0) + { + jobject bbufTemp; + bbufTemp = (*env)->CallObjectMethod(env, bbuf, set_position_mid, + buf->position + buf->count); + (*env)->DeleteLocalRef(env, bbufTemp); + } + + switch (buf->type) + { + case DIRECT: + break; + case ARRAY: + arr = (*env)->CallObjectMethod(env, bbuf, array_mid); + (*env)->ReleaseByteArrayElements(env, arr, buf->ptr, action); + (*env)->DeleteLocalRef(env, arr); + break; + case UNKNOWN: + /* TODO: Handle buffers that are not direct or array backed */ + break; + } +} + +void +JCL_cleanup_buffers(JNIEnv *env, + struct JCL_buffer *bi_list, + jint vec_len, + jobjectArray bbufs, + jint offset, + jlong num_bytes) +{ + jint i; + + /* Update all of the bbufs with the approriate information */ + for (i = 0; i < vec_len; i++) + { + struct JCL_buffer* buf; + jobject bbuf; + + buf = &bi_list[i]; + bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i); + + if (num_bytes > (buf->limit - buf->position)) + buf->count = (buf->limit - buf->position); + else + buf->count = num_bytes; + + num_bytes -= buf->count; + + JCL_release_buffer(env, buf, bbuf, JNI_ABORT); + (*env)->DeleteLocalRef(env, bbuf); + } +} + + +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_initIDs (JNIEnv *env, + jclass clazz __attribute__ ((__unused__))) +{ + jclass bufferClass = JCL_FindClass(env, "java/nio/Buffer"); + jclass byteBufferClass = JCL_FindClass(env, "java/nio/ByteBuffer"); + + address_fid = (*env)->GetFieldID(env, bufferClass, "address", + "Lgnu/classpath/Pointer;"); + if (address_fid == NULL) + { + JCL_ThrowException(env, "java/lang/InternalError", + "Unable to find internal field"); + return; + } + + get_position_mid = get_method_id(env, bufferClass, "position", "()I"); + set_position_mid = get_method_id(env, bufferClass, "position", + "(I)Ljava/nio/Buffer;"); + get_limit_mid = get_method_id(env, bufferClass, "limit", "()I"); + set_limit_mid = get_method_id(env, bufferClass, "limit", + "(I)Ljava/nio/Buffer;"); + has_array_mid = get_method_id(env, byteBufferClass, "hasArray", "()Z"); + array_mid = get_method_id(env, byteBufferClass, "array", "()[B"); + array_offset_mid = get_method_id(env, byteBufferClass, "arrayOffset", "()I"); +} + +JNIEXPORT void JNICALL +Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jboolean blocking) +{ + int opts; + + opts = fcntl(fd, F_GETFL); + if (opts < 0) + { + JCL_ThrowException(env, IO_EXCEPTION, + "Failed to get flags for file desriptor"); + return; + } + + if (blocking) + opts |= O_NONBLOCK; + else + opts &= ~(O_NONBLOCK); + + opts = fcntl(fd, F_SETFL, opts); + + if (opts < 0) + { + JCL_ThrowException(env, IO_EXCEPTION, + "Failed to set flags for file desriptor"); + return; + } +} + + +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_read (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jobject bbuf) +{ + jint len; + ssize_t result; + struct JCL_buffer buf; + + if (JCL_init_buffer(env, &buf, bbuf) < 0) + { + /* TODO: Rethrown exception */ + JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed"); + return -1; + } + + len = buf.limit - buf.position; + + result = read(fd, &(buf.ptr[buf.position + buf.offset]), len); + buf.count = result; + + if (result == 0) + result = -1; /* End Of File */ + else if (result == -1) + { + buf.count = 0; + if (errno == EAGAIN) /* Non-blocking */ + result = 0; + else if (errno == EBADF) /* Bad fd */ + { + JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); + JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, + strerror(errno)); + return -1; + } + else + { + JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); + JCL_ThrowException (env, IO_EXCEPTION, strerror(errno)); + return -1; + } + } + else + + JCL_release_buffer(env, &buf, bbuf, JNI_COMMIT); + + return result; +} + +JNIEXPORT jint JNICALL +Java_gnu_java_nio_VMChannel_write (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jobject bbuf) +{ + jint len; + ssize_t result; + struct JCL_buffer buf; + + if (JCL_init_buffer(env, &buf, bbuf) < 0) + { + /* TODO: Rethrown exception */ + JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed"); + return -1; + } + + len = buf.limit - buf.position; + + result = write(fd, &(buf.ptr[buf.position + buf.offset]), len); + buf.count = result; + + if (result == -1) + { + if (errno == EAGAIN) /* Non-blocking */ + result = 0; + else + { + JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); + JCL_ThrowException(env, IO_EXCEPTION, strerror(errno)); + return -1; + } + } + + JCL_release_buffer(env, &buf, bbuf, JNI_ABORT); + + return result; +} + + +/* + * Implementation of a scattering read. Will use the appropriate + * vector based read call (currently readv on Linux). + * + * This has a limit to the number of buffers that will be read. It + * will not make muliple readv calls. This is to ensure that operations + * are atomic. Currently it is limited to 16 buffers. This is for + * compatibiliy with Sun. + */ +JNIEXPORT jlong JNICALL +Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jobjectArray bbufs, + jint offset, + jint length) +{ + jint i; + jboolean is_error = JNI_FALSE; + char *error_msg; + struct iovec buffers[JCL_IOV_MAX]; + struct JCL_buffer bi_list[JCL_IOV_MAX]; + ssize_t result; + jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX; + jlong bytes_read = 0; + + /* Build the vector of buffers to read into */ + for (i = 0; i < vec_len; i++) + { + struct JCL_buffer* buf; + jobject bbuf; + + buf = &bi_list[i]; + bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i); + + JCL_init_buffer(env, buf, bbuf); + + buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]); + buffers[i].iov_len = buf->limit - buf->position; + (*env)->DeleteLocalRef(env, bbuf); + } + + /* Work the scattering magic */ + result = readv(fd, buffers, vec_len); + bytes_read = (jlong) result; + + /* Handle the response */ + if (result < 0) + { + if (errno == EAGAIN) /* Non blocking */ + result = 0; + else if (errno == EBADF) /* Bad fd */ + { + JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read); + JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, + strerror(errno)); + return -1; + } + else + { + JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read); + JCL_ThrowException (env, IO_EXCEPTION, strerror(errno)); + return -1; + } + bytes_read = 0; + } + else if (result == 0) /* EOF */ + { + result = -1; + } + + JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read); + + return (jlong) result; +} + +/* + * Implementation of a gathering write. Will use the appropriate + * vector based read call (currently readv on Linux). + * + * This has a limit to the number of buffers that will be read. It + * will not make muliple readv calls. This is to ensure that operations + * are atomic. Currently it is limited to 16 buffers. This is for + * compatibiliy with Sun. + */ +JNIEXPORT jlong JNICALL +Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, + jobject o __attribute__ ((__unused__)), + jint fd, + jobjectArray bbufs, + jint offset, + jint length) +{ + int i; + jboolean is_error = JNI_FALSE; + char *error_msg; + struct iovec buffers[JCL_IOV_MAX]; + struct JCL_buffer bi_list[JCL_IOV_MAX]; + ssize_t result; + jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX; + jlong bytes_written; + + + /* Build the vector of buffers to read into */ + for (i = 0; i < vec_len; i++) + { + struct JCL_buffer* buf; + jobject bbuf; + + buf = &bi_list[i]; + bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i); + + JCL_init_buffer(env, buf, bbuf); + + buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]); + buffers[i].iov_len = buf->limit - buf->position; + (*env)->DeleteLocalRef(env, bbuf); + } + + /* Work the gathering magic */ + result = writev(fd, buffers, vec_len); + bytes_written = (jlong) result; + + if (result < 0) + { + bytes_written = 0; + if (errno == EAGAIN) /* Non blocking */ + result = 0; + else if (errno == EBADF) /* Bad fd */ + { + JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, + bytes_written); + JCL_ThrowException (env, NON_WRITABLE_CHANNEL_EXCEPTION, + strerror(errno)); + return -1; + } + else + { + JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, + bytes_written); + JCL_ThrowException (env, IO_EXCEPTION, strerror(errno)); + return -1; + } + } + else if (result == 0) /* EOF?? Does this happen on a write */ + result = -1; + + JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_written); + return (jlong) result; +} + + + + +#ifdef __cplusplus +} +#endif diff --git a/vm/reference/gnu/java/nio/VMChannel.java b/vm/reference/gnu/java/nio/VMChannel.java new file mode 100644 index 000000000..fdea8ff62 --- /dev/null +++ b/vm/reference/gnu/java/nio/VMChannel.java @@ -0,0 +1,197 @@ +/* VMChannel.java -- Native interface suppling channel operations. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.nio; + +import gnu.classpath.Configuration; +import gnu.java.net.PlainSocketImpl; +import gnu.java.nio.PipeImpl.SinkChannelImpl; +import gnu.java.nio.PipeImpl.SourceChannelImpl; +import gnu.java.nio.channels.FileChannelImpl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Native interface to support configuring of channel to run in a non-blocking + * manner and support scatter/gather io operations. + * + * @author Michael Barker + * + */ +public class VMChannel +{ + private final int fd; + + private VMChannel(int fd) + { + this.fd = fd; + } + + public static VMChannel getVMChannel(PlainSocketImpl socket) + { + return new VMChannel(socket.getNativeFD()); + } + + public static VMChannel getVMChannel(SourceChannelImpl source) + { + return new VMChannel(source.getNativeFD()); + } + + public static VMChannel getVMChannel(SinkChannelImpl sink) + { + return new VMChannel(sink.getNativeFD()); + } + + public static VMChannel getVMChannel(FileChannelImpl file) + { + return new VMChannel(file.getNativeFD()); + } + + static + { + // load the shared library needed for native methods. + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javanio"); + } + initIDs(); + } + + /** + * Set the file descriptor to have the required blocking + * setting. + * + * @param fd + * @param blocking + */ + public native void setBlocking(int fd, boolean blocking); + + public void setBlocking(boolean blocking) + { + setBlocking(fd, blocking); + } + + + /** + * Reads a byte buffer directly using the supplied file descriptor. + * Assumes that the buffer is a DirectBuffer. + * + * @param fd Native file descriptor to read from. + * @param dst Direct Byte Buffer to read to. + * @return Number of bytes read. + * @throws IOException If an error occurs or dst is not a direct buffers. + */ + native int read(int fd, ByteBuffer dst) + throws IOException; + + public int read(ByteBuffer dst) + throws IOException + { + return read(fd, dst); + } + + /** + * Reads into byte buffers directly using the supplied file descriptor. + * Assumes that the buffer list contains DirectBuffers. Will perform a + * scattering read. + * + * @param fd Native file descriptor to read from. + * @param dsts An array direct byte buffers. + * @param offset Index of the first buffer to read to. + * @param length The number of buffers to read to. + * @return Number of bytes read. + * @throws IOException If an error occurs or the dsts are not direct buffers. + */ + native long readScattering(int fd, ByteBuffer[] dsts, int offset, int length) + throws IOException; + + public long readScattering(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if (offset + length > dsts.length) + throw new IndexOutOfBoundsException("offset + length > dsts.length"); + + return readScattering(fd, dsts, offset, length); + } + + /** + * Writes from a direct byte bufer using the supplied file descriptor. + * Assumes the buffer is a DirectBuffer. + * + * @param fd + * @param src + * @return Number of bytes written. + * @throws IOException + */ + native int write(int fd, ByteBuffer src) + throws IOException; + + public int write(ByteBuffer src) + throws IOException + { + return write(fd, src); + } + + /** + * Writes from byte buffers directly using the supplied file descriptor. + * Assumes the that buffer list constains DirectBuffers. Will perform + * as gathering write. + * + * @param fd + * @param srcs + * @param offset + * @param length + * @return Number of bytes written. + * @throws IOException + */ + native long writeGathering(int fd, ByteBuffer[] srcs, int offset, int length) + throws IOException; + + public long writeGathering(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if (offset + length > srcs.length) + throw new IndexOutOfBoundsException("offset + length > srcs.length"); + + return writeGathering(fd, srcs, offset, length); + } + + private native static void initIDs(); + +} -- cgit v1.2.1