--- old/src/java.base/share/classes/module-info.java 2018-06-22 08:18:56.000000000 +0100 +++ new/src/java.base/share/classes/module-info.java 2018-06-22 08:18:55.000000000 +0100 @@ -242,6 +242,7 @@ exports sun.nio.ch to java.management, jdk.crypto.cryptoki, + jdk.net, jdk.sctp, jdk.unsupported; exports sun.nio.cs to --- old/src/jdk.net/share/classes/module-info.java 2018-06-22 08:18:58.000000000 +0100 +++ new/src/jdk.net/share/classes/module-info.java 2018-06-22 08:18:57.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -31,5 +31,6 @@ */ module jdk.net { exports jdk.net; + exports jdk.nio; } --- /dev/null 2018-06-22 08:18:59.000000000 +0100 +++ new/src/jdk.net/share/classes/jdk/nio/Channels.java 2018-06-22 08:18:59.000000000 +0100 @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2018, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.nio; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Objects; + +import sun.nio.ch.IOUtil; +import sun.nio.ch.Net; +import sun.nio.ch.SelChImpl; +import sun.nio.ch.SelectionKeyImpl; +import sun.nio.ch.SelectorProviderImpl; + +/** + * Defines static methods to create {@link java.nio.channels.Channel channels}. + * + *

Unless otherwise specified, passing a {@code null} argument to any of the + * methods defined here will cause a {@code NullPointerException} to be thrown. + * + * @since 11 + */ + +public final class Channels { + private Channels() { } + + /** + * An object used to coordinate the closing of a selectable channel created + * by {@link Channels#readWriteSelectableChannel readWriteSelectableChannel}. + * + * @since 11 + */ + public interface SelectableChannelCloser { + + /** + * Closes a selectable channel. + * + *

This method is invoked by the channel's close method in order to + * perform the actual work of closing the channel. This method is only + * invoked if the channel has not yet been closed, and it is never + * invoked more than once by the channel's close implementation. + * + *

An implementation of this method must arrange for any other + * thread that is blocked in an I/O operation upon the channel to return + * immediately, either by throwing an exception or by returning normally. + * If the channel is {@link SelectableChannel#isRegistered registered} + * with one or more {@link java.nio.channels.Selector Selector}s then + * the file descriptor should not be released until the {@link + * #implReleaseChannel implReleaseChannel} method is invoked.

+ * + * @param sc + * The selectable channel + * + * @throws IOException + * If an I/O error occurs while closing the file descriptor + * + * @see java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel + */ + void implCloseChannel(SelectableChannel sc) throws IOException; + + /** + * Release the file descriptor and any resources for a selectable + * channel that closed while registered with one or more {@link + * java.nio.channels.Selector Selector}s. + * + *

This method is for cases where a channel is closed when + * {@link java.nio.channels.SelectableChannel#isRegistered registered} + * with one or more {@code Selector}s. A channel may remain registered + * for some time after it is closed. This method is invoked when the + * channel is eventually deregistered from the last {@code Selector} + * that it was registered with. It is invoked at most once. + * + * @apiNote This method is invoked while synchronized on the selector + * and its selected-key set. Great care must be taken to avoid deadlocks + * with other threads that also synchronize on these objects. + * + * @param sc + * The closed selectable channel + * + * @throws IOException + * If an I/O error occurs + * + * @see java.nio.channels.spi.AbstractSelector#deregister + */ + void implReleaseChannel(SelectableChannel sc) throws IOException; + } + + /** + * Creates a selectable channel to a file descriptor that supports an + * {@link SelectableChannel#validOps() operation-set} of + * {@link SelectionKey#OP_READ OP_READ} and + * {@link SelectionKey#OP_WRITE OP_WRITE}. The selectable channel will be + * created by the default {@link SelectorProvider}. + * + *

The given file descriptor is a socket or resource that can be + * multiplexed by a {@link java.nio.channels.Selector} for read and write + * readiness. Great care is required to coordinate direct use of the file + * descriptor with the use of the selectable channel. In particular, + * changing the blocking mode or closing the file descriptor without careful + * coordination will result in unspecified and unsafe side effects. The + * given {@link SelectableChannelCloser SelectableChannelCloser} is invoked to + * close the file descriptor and to coordinate the closing when the channel + * is registered with a {@code Selector}.

+ * + *

If there is a security manager set then its + * {@link SecurityManager#checkRead(FileDescriptor) checkRead} and + * {@link SecurityManager#checkWrite(FileDescriptor) checkWrite} methods + * are invoked to check that the caller has permission to both read from and + * write to the file descriptor.

+ * + * @implNote This method throws {@code UnsupportedOperationException} if + * the default {@code SelectorProvider} is not the JDK built-in implementation. + * + * @param fd + * The file descriptor + * @param closer + * The object to close the channel + * + * @return The selectable channel + * + * @throws IllegalArgumentException + * If the file descriptor is not {@link FileDescriptor#valid() valid} + * @throws SecurityException + * If denied by the security manager + */ + public static SelectableChannel readWriteSelectableChannel(FileDescriptor fd, + SelectableChannelCloser closer) { + Objects.requireNonNull(closer); + if (!fd.valid()) + throw new IllegalArgumentException("file descriptor is not valid"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(fd); + sm.checkWrite(fd); + } + + SelectorProvider provider = SelectorProvider.provider(); + if (!(provider instanceof SelectorProviderImpl)) + throw new UnsupportedOperationException("custom SelectorProvider"); + + return new ReadWriteChannelImpl((SelectorProviderImpl)provider, fd, closer); + } + + private static final class ReadWriteChannelImpl + extends AbstractSelectableChannel implements SelChImpl + { + private final FileDescriptor fd; + private final int fdVal; + private final SelectableChannelCloser closer; + + ReadWriteChannelImpl(SelectorProviderImpl provider, + FileDescriptor fd, + SelectableChannelCloser closer) { + super(provider); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + this.closer = closer; + } + + @Override + public FileDescriptor getFD() { + return fd; + } + + @Override + public int getFDVal() { + return fdVal; + } + + @Override + public int validOps() { + return (SelectionKey.OP_READ | SelectionKey.OP_WRITE); + } + + private boolean translateReadyOps(int ops, + int initialOps, + SelectionKeyImpl ski) { + int intOps = ski.nioInterestOps(); + int oldOps = ski.nioReadyOps(); + int newOps = initialOps; + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0)) + newOps |= SelectionKey.OP_READ; + + if (((ops & Net.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0)) + newOps |= SelectionKey.OP_WRITE; + + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + @Override + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, ski.nioReadyOps(), ski); + } + + @Override + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, 0, ski); + } + + @Override + public int translateInterestOps(int ops) { + int newOps = 0; + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= Net.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= Net.POLLOUT; + return newOps; + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + closer.implCloseChannel(this); + } + + @Override + public void kill() throws IOException { + closer.implReleaseChannel(this); + } + } +} --- /dev/null 2018-06-22 08:19:01.000000000 +0100 +++ new/src/jdk.net/share/classes/jdk/nio/package-info.java 2018-06-22 08:19:00.000000000 +0100 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/** + * Defines JDK-specific {@link java.nio.channels.Channel channel} APIs. + * + * @since 11 + */ + +package jdk.nio; --- /dev/null 2018-06-22 08:19:03.000000000 +0100 +++ new/test/jdk/jdk/nio/Basic.java 2018-06-22 08:19:02.000000000 +0100 @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018, 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 + * @bug 8198372 + * @modules jdk.net java.base/sun.nio.ch:+open + * @run testng Basic + * @summary Basic tests for jdk.nio.Channels + */ + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import jdk.nio.Channels; +import jdk.nio.Channels.SelectableChannelCloser; + +import sun.nio.ch.IOUtil; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class Basic { + + /** + * A loopback connection + */ + static class Connection implements Closeable { + private final SocketChannel sc1; + private final SocketChannel sc2; + + private Connection(SocketChannel sc1, SocketChannel sc2) { + this.sc1 = sc1; + this.sc2 = sc2; + } + + static Connection open() throws IOException { + try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + InetAddress lb = InetAddress.getLoopbackAddress(); + ssc.bind(new InetSocketAddress(lb, 0)); + SocketChannel sc1 = SocketChannel.open(ssc.getLocalAddress()); + SocketChannel sc2 = ssc.accept(); + return new Connection(sc1, sc2); + } + } + + SocketChannel channel1() { + return sc1; + } + + SocketChannel channel2() { + return sc2; + } + + public void close() throws IOException { + try { + sc1.close(); + } finally { + sc2.close(); + } + } + } + + /** + * A SelectableChannelCloser that tracks if the implCloseChannel and + * implReleaseChannel methods are invoked + */ + static class Closer implements SelectableChannelCloser { + int closeCount; + SelectableChannel invokedToClose; + int releaseCount; + SelectableChannel invokedToRelease; + + @Override + public void implCloseChannel(SelectableChannel sc) { + closeCount++; + invokedToClose = sc; + } + + @Override + public void implReleaseChannel(SelectableChannel sc) { + releaseCount++; + invokedToRelease = sc; + } + } + + /** + * Basic test of channel registered with Selector + */ + public void testSelect() throws IOException { + Selector sel = Selector.open(); + try (Connection connection = Connection.open()) { + + // create channel with the file descriptor from one end of the connection + FileDescriptor fd = getFD(connection.channel1()); + SelectableChannel ch = Channels.readWriteSelectableChannel(fd, new Closer()); + + // register for read events, channel should not be selected + ch.configureBlocking(false); + SelectionKey key = ch.register(sel, SelectionKey.OP_READ); + int n = sel.selectNow(); + assertTrue(n == 0); + + // write bytes to other end of connection + SocketChannel peer = connection.channel2(); + ByteBuffer msg = ByteBuffer.wrap("hello".getBytes("UTF-8")); + int nwrote = peer.write(msg); + assertTrue(nwrote >= 0); + + // channel should be selected + n = sel.select(); + assertTrue(n == 1); + assertTrue(sel.selectedKeys().contains(key)); + assertTrue(key.isReadable()); + assertFalse(key.isWritable()); + sel.selectedKeys().clear(); + + // change interest set for writing, channel should be selected + key.interestOps(SelectionKey.OP_WRITE); + n = sel.select(); + assertTrue(n == 1); + assertTrue(sel.selectedKeys().contains(key)); + assertTrue(key.isWritable()); + assertFalse(key.isReadable()); + sel.selectedKeys().clear(); + + // change interest set for reading + writing, channel should be selected + key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); + n = sel.select(); + assertTrue(n == 1); + assertTrue(sel.selectedKeys().contains(key)); + assertTrue(key.isWritable()); + assertTrue(key.isReadable()); + sel.selectedKeys().clear(); + + // change interest set to 0 to deregister, channel should not be selected + key.interestOps(0); + n = sel.selectNow(); + assertTrue(n == 0); + + } finally { + sel.close(); + } + } + + /** + * Test that the SelectableChannelCloser implCloseChannel method is invoked. + */ + public void testImplCloseChannel() throws IOException { + try (Connection connection = Connection.open()) { + FileDescriptor fd = getFD(connection.channel1()); + Closer closer = new Closer(); + SelectableChannel ch = Channels.readWriteSelectableChannel(fd, closer); + + // close channel twice, checking that the closer is invoked only once + for (int i=0; i<2; i++) { + ch.close(); + + // implCloseChannel should been invoked once + assertTrue(closer.closeCount == 1); + assertTrue(closer.invokedToClose == ch); + + // implReleaseChannel should not have been invoked + assertTrue(closer.releaseCount == 0); + } + } + } + + /** + * Test that the SelectableChannelCloser implReleaseChannel method is invoked. + */ + public void testImplReleaseChannel() throws IOException { + Selector sel = Selector.open(); + try (Connection connection = Connection.open()) { + FileDescriptor fd = getFD(connection.channel1()); + Closer closer = new Closer(); + SelectableChannel ch = Channels.readWriteSelectableChannel(fd, closer); + + // register with Selector, invoking selectNow to ensure registered + ch.configureBlocking(false); + ch.register(sel, SelectionKey.OP_WRITE); + sel.selectNow(); + + // close channel + ch.close(); + + // implCloseChannel should have been invoked + assertTrue(closer.closeCount == 1); + assertTrue(closer.invokedToClose == ch); + + // implReleaseChannel should not have been invoked + assertTrue(closer.releaseCount == 0); + + // flush the selector + sel.selectNow(); + + // implReleaseChannel should have been invoked + assertTrue(closer.releaseCount == 1); + assertTrue(closer.invokedToRelease == ch); + + } finally { + sel.close(); + } + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testInvalidFileDescriptor() throws IOException { + FileDescriptor fd = IOUtil.newFD(-1); + Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() { + @Override + public void implCloseChannel(SelectableChannel sc) { } + @Override + public void implReleaseChannel(SelectableChannel sc) { } + }); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullFileDescriptor() throws IOException { + Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() { + @Override + public void implCloseChannel(SelectableChannel sc) { } + @Override + public void implReleaseChannel(SelectableChannel sc) { } + }); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullCloser() throws IOException { + try (Connection connection = Connection.open()) { + FileDescriptor fd = getFD(connection.channel1()); + Channels.readWriteSelectableChannel(fd, null); + } + } + + private static FileDescriptor getFD(SocketChannel sc) { + try { + Class clazz = sc.getClass(); + Field f = clazz.getDeclaredField("fd"); + f.setAccessible(true); + return (FileDescriptor) f.get(sc); + } catch (Exception e) { + throw new Error(e); + } + } +}