I'd like to backport the KQueue based NIO selector Alex Strange recently committed to the MacOS X port
http://hg.openjdk.java.net/macosx-port/macosx-port/jdk/rev/cb5ebc902d33 The attached patch compiles on FreeBSD and should work fine on MacOS X (since I just pulled the code in from the MacOS X port :). Can someone test on OpenBSD and/or NetBSD? -- Greg Lewis Email : gle...@eyesbeyond.com Eyes Beyond Web : http://www.eyesbeyond.com Information Technology FreeBSD : gle...@freebsd.org
diff -r 0870207843e2 make/java/nio/Makefile --- a/make/java/nio/Makefile Sun Sep 11 10:51:01 2011 -0700 +++ b/make/java/nio/Makefile Thu Sep 22 23:15:12 2011 -0700 @@ -264,6 +264,9 @@ FILES_java += \ sun/nio/ch/AbstractPollSelectorImpl.java \ sun/nio/ch/InheritedChannel.java \ + sun/nio/ch/KQueueArrayWrapper.java \ + sun/nio/ch/KQueueSelectorImpl.java \ + sun/nio/ch/KQueueSelectorProvider.java \ sun/nio/ch/PollSelectorProvider.java \ sun/nio/ch/PollSelectorImpl.java \ sun/nio/ch/Port.java \ @@ -298,6 +301,7 @@ FILES_c += \ InheritedChannel.c \ + KQueueArrayWrapper.c \ NativeThread.c \ PollArrayWrapper.c \ UnixAsynchronousServerSocketChannelImpl.c \ @@ -318,7 +322,7 @@ sun/nio/fs/BsdNativeDispatcher.java \ sun/nio/fs/UnixCopyFile.java \ sun/nio/fs/UnixNativeDispatcher.java - + FILES_gen += \ sun/nio/fs/UnixConstants.java endif # PLATFORM = bsd diff -r 0870207843e2 src/solaris/classes/sun/nio/ch/DefaultSelectorProvider.java --- a/src/solaris/classes/sun/nio/ch/DefaultSelectorProvider.java Sun Sep 11 10:51:01 2011 -0700 +++ b/src/solaris/classes/sun/nio/ch/DefaultSelectorProvider.java Thu Sep 22 23:15:12 2011 -0700 @@ -69,6 +69,10 @@ } } + if (osname.contains("BSD") || osname.contains("Mac OS X")) { + return new sun.nio.ch.KQueueSelectorProvider(); + } + return new sun.nio.ch.PollSelectorProvider(); } diff -r 0870207843e2 src/solaris/classes/sun/nio/ch/KQueueArrayWrapper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/solaris/classes/sun/nio/ch/KQueueArrayWrapper.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * KQueueArrayWrapper.java + * Implementation of Selector using FreeBSD / Mac OS X kqueues + * Derived from Sun's DevPollArrayWrapper + */ + +package sun.nio.ch; + +import sun.misc.*; +import java.io.IOException; +import java.io.FileDescriptor; + + +/* + * struct kevent { // 32-bit 64-bit + * uintptr_t ident; // 4 8 + * short filter; // 2 2 + * u_short flags; // 2 2 + * u_int fflags; // 4 4 + * intptr_t data; // 4 8 + * void *udata; // 4 8 + * } // Total: 20 32 + * + * The implementation works in 32-bit and 64-bit world. We do this by calling a + * native function that actually sets the sizes and offsets of the fields based + * on which mode we're in. + */ + +class KQueueArrayWrapper { + // Event masks + static final short POLLIN = AbstractPollArrayWrapper.POLLIN; + static final short POLLOUT = AbstractPollArrayWrapper.POLLOUT; + + // kevent filters + static short EVFILT_READ; + static short EVFILT_WRITE; + + // kevent struct + // These fields are now set by initStructSizes in the static initializer. + static short SIZEOF_KEVENT; + static short FD_OFFSET; + static short FILTER_OFFSET; + + // kevent array size (just under 1K bytes) + static final int NUM_KEVENTS = 50; + + // Are we in a 64-bit VM? + static boolean is64bit = false; + + // The kevent array (used for outcoming events only) + private AllocatedNativeObject keventArray = null; + private long keventArrayAddress; + + // The kqueue fd + private int kq = -1; + + // The fd of the interrupt line going out + private int outgoingInterruptFD; + + // The fd of the interrupt line coming in + private int incomingInterruptFD; + + static { + initStructSizes(); + String datamodel = (String) java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("sun.arch.data.model")); + is64bit = datamodel.equals("64"); + } + + KQueueArrayWrapper() { + int allocationSize = SIZEOF_KEVENT * NUM_KEVENTS; + keventArray = new AllocatedNativeObject(allocationSize, true); + keventArrayAddress = keventArray.address(); + kq = init(); + } + + void initInterrupt(int fd0, int fd1) { + outgoingInterruptFD = fd1; + incomingInterruptFD = fd0; + register0(kq, fd0, 1, 0); + } + + int getReventOps(int index) { + int result = 0; + int offset = SIZEOF_KEVENT*index + FILTER_OFFSET; + short filter = keventArray.getShort(offset); + + // This is all that's necessary based on inspection of usage: + // SinkChannelImpl, SourceChannelImpl, DatagramChannelImpl, + // ServerSocketChannelImpl, SocketChannelImpl + if (filter == EVFILT_READ) { + result |= POLLIN; + } else if (filter == EVFILT_WRITE) { + result |= POLLOUT; + } + + return result; + } + + int getDescriptor(int index) { + int offset = SIZEOF_KEVENT*index + FD_OFFSET; + /* The ident field is 8 bytes in 64-bit world, however the API wants us + * to return an int. Hence read the 8 bytes but return as an int. + */ + if (is64bit) { + long fd = keventArray.getLong(offset); + assert fd <= Integer.MAX_VALUE; + return (int) fd; + } else { + return keventArray.getInt(offset); + } + } + + void setInterest(int fd, int events) { + register0(kq, fd, events & POLLIN, events & POLLOUT); + } + + void release(int fd) { + register0(kq, fd, 0, 0); + } + + void close() throws IOException { + if (keventArray != null) { + keventArray.free(); + keventArray = null; + } + if (kq >= 0) { + FileDispatcherImpl.closeIntFD(kq); + kq = -1; + } + } + + int poll(long timeout) { + int updated = kevent0(kq, keventArrayAddress, NUM_KEVENTS, timeout); + return updated; + } + + void interrupt() { + interrupt(outgoingInterruptFD); + } + + private native int init(); + private static native void initStructSizes(); + + private native void register0(int kq, int fd, int read, int write); + private native int kevent0(int kq, long keventAddress, int keventCount, + long timeout); + private static native void interrupt(int fd); +} + diff -r 0870207843e2 src/solaris/classes/sun/nio/ch/KQueueSelectorImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/solaris/classes/sun/nio/ch/KQueueSelectorImpl.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * KQueueSelectorImpl.java + * Implementation of Selector using FreeBSD / Mac OS X kqueues + * Derived from Sun's DevPollSelectorImpl + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.FileDescriptor; +import java.nio.channels.*; +import java.nio.channels.spi.*; +import java.util.*; +import sun.misc.*; + +class KQueueSelectorImpl + extends SelectorImpl +{ + // File descriptors used for interrupt + protected int fd0; + protected int fd1; + + // The kqueue manipulator + KQueueArrayWrapper kqueueWrapper; + + // Count of registered descriptors (including interrupt) + private int totalChannels; + + // Map from file descriptors to selection keys + private HashMap fdToKey; + + // True if this Selector has been closed + private boolean closed = false; + + // Lock for interrupt triggering and clearing + private Object interruptLock = new Object(); + private boolean interruptTriggered = false; + + /** + * Package private constructor called by factory method in + * the abstract superclass Selector. + */ + KQueueSelectorImpl(SelectorProvider sp) { + super(sp); + long fds = IOUtil.makePipe(false); + fd0 = (int)(fds >>> 32); + fd1 = (int)fds; + kqueueWrapper = new KQueueArrayWrapper(); + kqueueWrapper.initInterrupt(fd0, fd1); + fdToKey = new HashMap(); + totalChannels = 1; + } + + + protected int doSelect(long timeout) + throws IOException + { + int entries = 0; + if (closed) + throw new ClosedSelectorException(); + processDeregisterQueue(); + if (timeout == 0 && totalChannels == 1) + return 0; + try { + begin(); + entries = kqueueWrapper.poll(timeout); + } finally { + end(); + } + processDeregisterQueue(); + return updateSelectedKeys(entries); + } + + + /** + * Update the keys whose fd's have been selected by the devpoll + * driver. Add the ready keys to the ready queue. + * If the interrupt fd has been selected, drain it and clear the interrupt. + */ + private int updateSelectedKeys(int entries) + throws IOException + { + int numKeysUpdated = 0; + boolean interrupted = false; + + for (int i = 0; i < entries; i++) { + int nextFD = kqueueWrapper.getDescriptor(i); + if (nextFD == fd0) { + interrupted = true; + } else { + SelectionKeyImpl ski = + (SelectionKeyImpl) fdToKey.get(new Integer(nextFD)); + // ski is null in the case of an interrupt + if (ski != null) { + int rOps = kqueueWrapper.getReventOps(i); + if (selectedKeys.contains(ski)) { + if (ski.channel.translateAndSetReadyOps(rOps, ski)) { + numKeysUpdated++; + } + } else { + ski.channel.translateAndSetReadyOps(rOps, ski); + if ((ski.readyOps() & ski.interestOps()) != 0) { + selectedKeys.add(ski); + numKeysUpdated++; + } + } + } + } + } + + if (interrupted) { + // Clear the wakeup pipe + synchronized (interruptLock) { + IOUtil.drain(fd0); + interruptTriggered = false; + } + } + + return numKeysUpdated; + } + + + protected void implClose() throws IOException { + if (!closed) { + closed = true; + FileDispatcherImpl.closeIntFD(fd0); + FileDispatcherImpl.closeIntFD(fd1); + if (kqueueWrapper != null) { + kqueueWrapper.release(fd0); + kqueueWrapper.close(); + kqueueWrapper = null; + selectedKeys = null; + + // Deregister channels + Iterator i = keys.iterator(); + while (i.hasNext()) { + SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); + deregister(ski); + SelectableChannel selch = ski.channel(); + if (!selch.isOpen() && !selch.isRegistered()) + ((SelChImpl)selch).kill(); + i.remove(); + } + totalChannels = 0; + } + fd0 = -1; + fd1 = -1; + } + } + + + protected void implRegister(SelectionKeyImpl ski) { + int fd = IOUtil.fdVal(ski.channel.getFD()); + fdToKey.put(new Integer(fd), ski); + totalChannels++; + keys.add(ski); + } + + + protected void implDereg(SelectionKeyImpl ski) throws IOException { + int fd = ski.channel.getFDVal(); + fdToKey.remove(new Integer(fd)); + kqueueWrapper.release(fd); + totalChannels--; + keys.remove(ski); + selectedKeys.remove(ski); + deregister((AbstractSelectionKey)ski); + SelectableChannel selch = ski.channel(); + if (!selch.isOpen() && !selch.isRegistered()) + ((SelChImpl)selch).kill(); + } + + + void putEventOps(SelectionKeyImpl ski, int ops) { + int fd = IOUtil.fdVal(ski.channel.getFD()); + kqueueWrapper.setInterest(fd, ops); + } + + + public Selector wakeup() { + synchronized (interruptLock) { + if (!interruptTriggered) { + kqueueWrapper.interrupt(); + interruptTriggered = true; + } + } + return this; + } + + + static { + Util.load(); + } +} + diff -r 0870207843e2 src/solaris/classes/sun/nio/ch/KQueueSelectorProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/solaris/classes/sun/nio/ch/KQueueSelectorProvider.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * KQueueSelectorProvider.java + * Implementation of Selector using FreeBSD / Mac OS X kqueues + * Derived from Sun's DevPollSelectorProvider + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.*; +import java.nio.channels.spi.*; + +public class KQueueSelectorProvider +extends SelectorProviderImpl +{ + public AbstractSelector openSelector() throws IOException { + return new KQueueSelectorImpl(this); + } +} diff -r 0870207843e2 src/solaris/native/sun/nio/ch/KQueueArrayWrapper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/solaris/native/sun/nio/ch/KQueueArrayWrapper.c Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * KQueueArrayWrapper.c + * Implementation of Selector using FreeBSD / Mac OS X kqueues + * Derived from Sun's DevPollArrayWrapper + */ + + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> + +JNIEXPORT void JNICALL +Java_sun_nio_ch_KQueueArrayWrapper_initStructSizes(JNIEnv *env, jclass clazz) +{ +#define CHECK_EXCEPTION() { \ + if ((*env)->ExceptionCheck(env)) { \ + goto exceptionOccurred; \ + } \ +} + +#define CHECK_ERROR_AND_EXCEPTION(_field) { \ + if (_field == NULL) { \ + goto badField; \ + } \ + CHECK_EXCEPTION(); \ +} + + + jfieldID field; + + field = (*env)->GetStaticFieldID(env, clazz, "EVFILT_READ", "S"); + CHECK_ERROR_AND_EXCEPTION(field); + (*env)->SetStaticShortField(env, clazz, field, EVFILT_READ); + CHECK_EXCEPTION(); + + field = (*env)->GetStaticFieldID(env, clazz, "EVFILT_WRITE", "S"); + CHECK_ERROR_AND_EXCEPTION(field); + (*env)->SetStaticShortField(env, clazz, field, EVFILT_WRITE); + CHECK_EXCEPTION(); + + field = (*env)->GetStaticFieldID(env, clazz, "SIZEOF_KEVENT", "S"); + CHECK_ERROR_AND_EXCEPTION(field); + (*env)->SetStaticShortField(env, clazz, field, (short) sizeof(struct kevent)); + CHECK_EXCEPTION(); + + field = (*env)->GetStaticFieldID(env, clazz, "FD_OFFSET", "S"); + CHECK_ERROR_AND_EXCEPTION(field); + (*env)->SetStaticShortField(env, clazz, field, (short) offsetof(struct kevent, ident)); + CHECK_EXCEPTION(); + + field = (*env)->GetStaticFieldID(env, clazz, "FILTER_OFFSET", "S"); + CHECK_ERROR_AND_EXCEPTION(field); + (*env)->SetStaticShortField(env, clazz, field, (short) offsetof(struct kevent, filter)); + CHECK_EXCEPTION(); + return; + +badField: + return; + +exceptionOccurred: + return; + +#undef CHECK_EXCEPTION +#undef CHECK_ERROR_AND_EXCEPTION +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_KQueueArrayWrapper_init(JNIEnv *env, jobject this) +{ + int kq = kqueue(); + if (kq < 0) { + JNU_ThrowIOExceptionWithLastError(env, "KQueueArrayWrapper: kqueue() failed"); + } + return kq; +} + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_KQueueArrayWrapper_register0(JNIEnv *env, jobject this, + jint kq, jint fd, jint r, jint w) +{ + struct kevent changes[2]; + struct kevent errors[2]; + struct timespec dontBlock = {0, 0}; + + // if (r) then { register for read } else { unregister for read } + // if (w) then { register for write } else { unregister for write } + // Ignore errors - they're probably complaints about deleting non- + // added filters - but provide an error array anyway because + // kqueue behaves erratically if some of its registrations fail. + EV_SET(&changes[0], fd, EVFILT_READ, r ? EV_ADD : EV_DELETE, 0, 0, 0); + EV_SET(&changes[1], fd, EVFILT_WRITE, w ? EV_ADD : EV_DELETE, 0, 0, 0); + kevent(kq, changes, 2, errors, 2, &dontBlock); +} + + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_KQueueArrayWrapper_kevent0(JNIEnv *env, jobject this, jint kq, + jlong kevAddr, jint kevCount, + jlong timeout) +{ + struct kevent *kevs = (struct kevent *)jlong_to_ptr(kevAddr); + struct timespec ts; + struct timespec *tsp; + int result; + + // Java timeout is in milliseconds. Convert to struct timespec. + // Java timeout == -1 : wait forever : timespec timeout of NULL + // Java timeout == 0 : return immediately : timespec timeout of zero + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; //nanosec = 1 million millisec + tsp = &ts; + } else { + tsp = NULL; + } + + result = kevent(kq, NULL, 0, kevs, kevCount, tsp); + + if (result < 0) { + if (errno == EINTR) { + // ignore EINTR, pretend nothing was selected + result = 0; + } else { + JNU_ThrowIOExceptionWithLastError(env, "KQueueArrayWrapper: kqueue failed"); + } + } + + return result; +} + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_KQueueArrayWrapper_interrupt(JNIEnv *env, jclass cls, jint fd) +{ + char c = 1; + if (1 != write(fd, &c, 1)) { + JNU_ThrowIOExceptionWithLastError(env, "KQueueArrayWrapper: interrupt failed"); + } +} + diff -r 0870207843e2 test/java/nio/channels/FileChannel/FileChannel_transferFrom.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/channels/FileChannel/FileChannel_transferFrom.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + @test + @summary Test of java.nio.Channel. + @summary com.apple.junit.java.nio.channels + @run main FileChannel_transferFrom + */ + +import junit.framework.*; + +import java.io.*; +import java.nio.channels.FileChannel; + +public class FileChannel_transferFrom extends TestCase +{ + public static Test suite() { + return new TestSuite( FileChannel_transferFrom.class); + } + + public static void main( String[] args ) { + junit.textui.TestRunner.run( suite() ); + } + + private static final String src = System.getProperty("harness.runner.home") + + File.separator + "data" + File.separator + "nio" + File.separator + "Franklin.txt"; + private String dst; + + public void testTransfer() throws Exception { + File f1 = new File(src); + assertNotNull(f1); + assertTrue("File not found:" + src, f1.exists()); + + File f2 = File.createTempFile("Ben", ".txt"); + assertNotNull(f2); + f2.deleteOnExit(); + dst = f2.getCanonicalPath(); + + // Create channel on the source + FileChannel srcChannel = new FileInputStream(src).getChannel(); + + // Create channel on the destination + FileChannel dstChannel = new FileOutputStream(dst).getChannel(); + + // Copy file contents from source to destination + dstChannel.transferFrom(srcChannel, 0, srcChannel.size()); + + // Close the channels + srcChannel.close(); + dstChannel.close(); + + assertTrue("Expected files to have same contents", contentsMatch() ); + } + + public boolean contentsMatch() throws Exception { + int data1, data2; + + FileInputStream original = new FileInputStream(src); + assertNotNull("Original file could not be opened", original); + + FileInputStream copy = new FileInputStream(dst); + assertNotNull("Copy file could not be opened", copy); + + do { + data1 = original.read(); + data2 = copy.read(); + if(data1 != data2) { + original.close(); + copy.close(); + return false; + } + } while(data1 != -1); + + return true; + } +} diff -r 0870207843e2 test/java/nio/channels/Selector/Kqueues64bitTest_R4258155.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/channels/Selector/Kqueues64bitTest_R4258155.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + @test + @summary Test to make sure kqueues work in 64-bit. We create two socket channels and write to them. + @summary We then use a Selector on those two channels and make sure select() returns us a non-zero value. + @summary com.apple.junit.java.nio.Selector + @run main Kqueues64bitTest_R4258155 + */ + +import junit.framework.*; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.*; +import java.util.Set; + +public class Kqueues64bitTest_R4258155 extends TestCase +{ + public static Test suite() { + return new TestSuite( Kqueues64bitTest_R4258155.class); + } + + public static void main( String[] args ) { + junit.textui.TestRunner.run( suite() ); + } + + public static final int PORT = 10000; + + static Object lock = new Object(); + static volatile boolean startTest = false; + static volatile boolean parttwo = false; + + public void test64bitKqueues() throws Exception { + String preferSelect = System.getProperty("java.nio.preferSelect", "false"); + if (preferSelect.equals("true")) { + System.err.println("WARNING! java.nio.preferSelect=true! We're not using kqueues. Test results could be incorrect!"); + } + + Selector selector = Selector.open(); + + ServerThread st = new ServerThread(); + st.start(); + + synchronized (Kqueues64bitTest_R4258155.lock) { + while (startTest == false) + Kqueues64bitTest_R4258155.lock.wait(); + } + + SocketChannel channel1 = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(), Kqueues64bitTest_R4258155.PORT)); + SocketChannel channel2 = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(), Kqueues64bitTest_R4258155.PORT)); + channel1.configureBlocking(false); + channel2.configureBlocking(false); + + SelectionKey channel1Key = channel1.register(selector, channel1.validOps()); + SelectionKey channel2Key = channel2.register(selector, channel2.validOps()); + + synchronized (Kqueues64bitTest_R4258155.lock) { + while (Kqueues64bitTest_R4258155.parttwo == false) + Kqueues64bitTest_R4258155.lock.wait(); + } + + int count = selector.select(); + assertFalse("Should get at least one selected channel", (count==0)); + // System.out.println("selector.select() returned : " + count); + Set <SelectionKey> selectedKeys = selector.selectedKeys(); + for (SelectionKey key : selectedKeys) { + boolean IKnowTheKey = key.equals(channel1Key) || key.equals(channel2Key); + assertTrue("Got a key I know nothing about", IKnowTheKey); + + } + selector.close(); + } +} + +class ServerThread extends Thread +{ + public void run() { + try { + ServerSocket server = new ServerSocket(Kqueues64bitTest_R4258155.PORT); + + synchronized (Kqueues64bitTest_R4258155.lock) { + Kqueues64bitTest_R4258155.startTest = true; + Kqueues64bitTest_R4258155.lock.notify(); + } + + // Wait for two connections + Socket conn1 = server.accept(); + Socket conn2 = server.accept(); + + + conn1.getOutputStream().write(61); + conn2.getOutputStream().write(62); + + synchronized (Kqueues64bitTest_R4258155.lock) { + Kqueues64bitTest_R4258155.parttwo = true; + Kqueues64bitTest_R4258155.lock.notify(); + } + + server.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } +} diff -r 0870207843e2 test/java/nio/channels/Selector/SelectorKqueueTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/channels/Selector/SelectorKqueueTest.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + @test + @summary com.apple.junit.java.nio.Selector + @library ../../regtesthelpers + @run main SelectorKqueueTest + */ + +import junit.framework.*; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.Iterator; + +public class SelectorKqueueTest extends TestCase +{ + public static final boolean DEBUG = false; + public static final int PORT = 1234; + + private Pipe signal; + private Thread listener; + private volatile boolean running; + + public static Test suite() { + return new TestSuite( SelectorKqueueTest.class); + } + + public static void main( String[] args ) { + junit.textui.TestRunner.run( suite() ); + } + + public void testSelector() { + SelectorKqueueTest test; + + try { + test=new SelectorKqueueTest(); + test.start(); + Thread.sleep(3000); + test.stop(); + } + catch( Throwable x ) { + x.printStackTrace(System.err); + } + } + + public SelectorKqueueTest() { + super(); + signal=null; + listener=null; + running=false; + } + + public void start() throws IOException { + if (DEBUG) {System.out.println("start");}; + + running=true; + + signal=Pipe.open(); + signal.source().configureBlocking(false); + + listener=new Thread() { + public void run() { + try { + listen(); + } + catch( Throwable x ) { + x.printStackTrace(System.err); + } + } + }; + listener.setPriority(Thread.NORM_PRIORITY); + listener.setDaemon(true); + listener.start(); + } + + public void stop() throws IOException, InterruptedException { + if (DEBUG) {System.out.println("stop");}; + + running=false; + signal(); + listener.join(); + + signal.sink().close(); + signal.source().close(); + } + + protected void listen() throws IOException { + ServerSocketChannel server; + Selector selector; + SelectionKey key; + Iterator iter; + int ret; + + if (DEBUG) {System.out.println("start listening");}; + + selector=Selector.open(); + + server=ServerSocketChannel.open(); + server.configureBlocking(false); + server.socket().setReuseAddress(true); + server.socket().bind(new InetSocketAddress(PORT),5); + + while (running) { + server.register(selector,SelectionKey.OP_ACCEPT); + signal.source().register(selector,SelectionKey.OP_READ); + + ret=selector.select(); + if (DEBUG) {System.out.println("got #"+ret);}; + + iter=selector.selectedKeys().iterator(); + while (iter.hasNext()) { + key=(SelectionKey) iter.next(); + iter.remove(); + + if (key.isValid() && key.isAcceptable()) { + if (DEBUG) {System.out.println(" accept");}; + } + + if (key.isValid() && key.isReadable()) { + if (DEBUG) {System.out.println(" read");}; + } + + if (key.isValid() && key.isWritable()) { + if (DEBUG) {System.out.println(" write");}; + } + } + } + + server.close(); + selector.close(); + + if (DEBUG) {System.out.println("stop listening");}; + } + + protected void signal() throws IOException { + byte[] dummy = new byte[] { (byte) 0 }; + + //System.out.println("signal server"); + signal.sink().write(ByteBuffer.wrap(dummy)); + } +} diff -r 0870207843e2 test/java/nio/channels/Selector/TestNIOLocalChannels.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/channels/Selector/TestNIOLocalChannels.java Thu Sep 22 23:15:12 2011 -0700 @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + @test + @summary This tests the basic functionality of sockets and selectors via local channels. + @summary It starts a server on one Thread, then it starts a client on another thread. + @summary It then checks for the validity of the data transfered betweenthem. + @summary If the data is not transmitted correctly the tests reports an error. + @summary com.apple.junit.java.nio.Selector + @library ../../regtesthelpers + @run main TestNIOLocalChannels + */ + +import junit.framework.*; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.Iterator; + +public class TestNIOLocalChannels extends TestCase +{ + static final int PORT = 10000; + + public static Object lock = new Object(); + public static boolean finishedReading = false; + public static int correctAnswers = 0; + + + protected void setUp() + { + new Server().start(); + + } + + protected void tearDown() + { + } + + public void testTestMe() throws Exception + { + int count = 0; + while (true) + { + count++; + synchronized(lock) + { + if (finishedReading) + { + assertTrue(" The transfered data from the server to the client didn't match exactly", correctAnswers > 50); + break; + } + } + Thread.sleep(200); + + // this means that the test hung - ABORT + if (count == 1000) + { + finishedReading = true; + assertTrue(" The test hung - ABORTING", false); + break; + } + } + } + + public static Test suite() + { + return new TestSuite(TestNIOLocalChannels.class); + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } +} + +/* + * Simple HTTP response server, adapted from O'Reilly NIO book's echo + * server example. + */ +class Server extends Thread { + + static final byte [] RESPONSE_BYTES = + "HTTP/1.0 200 OK\r\nContent-Type: image/gif\r\nContent-Length: 2\r\nServer: D1\r\nConnection: close\r\n\r\nHello".getBytes(); + + final ByteBuffer mRequestBuf; + final ByteBuffer mResponseBuf; + + Server() { + mRequestBuf = ByteBuffer.allocateDirect(4096); + mResponseBuf = ByteBuffer.allocateDirect(RESPONSE_BYTES.length); + mResponseBuf.put(RESPONSE_BYTES); + mResponseBuf.flip(); + } + + public void run() { + try { + + final ServerSocketChannel serverChannel = ServerSocketChannel.open(); + + serverChannel.socket().bind(new InetSocketAddress(TestNIOLocalChannels.PORT)); + serverChannel.configureBlocking(false); + + final Selector selector = Selector.open(); + serverChannel.register(selector, SelectionKey.OP_ACCEPT); + + // now we can run our client thread + new Client().start(); + + while (true) { + + synchronized(TestNIOLocalChannels.lock) + { + if (TestNIOLocalChannels.finishedReading) + { + break; + } + } + + // Block and spin until at least one key is ready. + if (selector.select(1000) == 0) + continue; + + final Iterator keyItr = selector.selectedKeys().iterator(); + while (keyItr.hasNext()) { + + final SelectionKey key = (SelectionKey) keyItr.next(); + + // Set new connections to readable. + if (key.isAcceptable()) { + final SocketChannel ch = ((ServerSocketChannel) key.channel()).accept(); + if (ch == null) + return; + ch.configureBlocking(false); + ch.register(selector, SelectionKey.OP_READ); + } + + // Handle readable connections. + else if (key.isReadable()) { + final SocketChannel ch = (SocketChannel) key.channel(); + ch.read(mRequestBuf); + mRequestBuf.clear(); // don't need data for testing. + mResponseBuf.rewind(); + ch.write(mResponseBuf); + ch.close(); + } + + // assert increment(); + keyItr.remove(); + } + } + + } catch (Exception e) { + System.err.println("PixelServer: exception in main select loop!"); + e.printStackTrace(); + } + } +} + +/** + * Simple HTTP client with statistics logging. + */ +class Client extends Thread { + public void run() { + try { + final byte [] buf = new byte[4096]; + char[] expectedResponse = { 'H', 'e', 'l', 'l', 'o' }; + int count = 0; + while (true) { + + synchronized(TestNIOLocalChannels.lock) + { + if (TestNIOLocalChannels.finishedReading) + { + break; + } + } + + final InputStream is = new URL("http://localhost:"+TestNIOLocalChannels.PORT+"/" + count).openStream(); + int num = 0; + while (num != -1) + { + num = is.read(buf); + for (int i = 0; i < num; i++) + { + if (((char) buf[i]) == expectedResponse[i]) + { + TestNIOLocalChannels.correctAnswers++; + } + else + { + TestNIOLocalChannels.correctAnswers = -1; + break; + } + } + } + is.close(); + + // we got 50 correct answers, we can break now + if (TestNIOLocalChannels.correctAnswers > 50) + { + break; + } + + if (TestNIOLocalChannels.correctAnswers == -1) + { + break; + } + // if (count % 5000 == 0) { + // long t = System.currentTimeMillis(); + // System.out.println("5k hits in " + (t - time) + "ms"); + // time = t; + // } + } + } catch (IOException e) { + e.printStackTrace(); + } + + synchronized(TestNIOLocalChannels.lock) + { + TestNIOLocalChannels.finishedReading = true; + } + } +} \ No newline at end of file diff -r 0870207843e2 test/sun/nio/ch/SelProvider.java --- a/test/sun/nio/ch/SelProvider.java Sun Sep 11 10:51:01 2011 -0700 +++ b/test/sun/nio/ch/SelProvider.java Thu Sep 22 23:15:12 2011 -0700 @@ -50,6 +50,8 @@ } else { throw new RuntimeException("Test does not recognize this operating system"); } + } else if (osname.contains("Mac OS X") || osname.contains("BSD")) { + expected = "sun.nio.ch.KQueueSelectorProvider"; } else return; if (!spName.equals(expected))