mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8198372: API to create a SelectableChannel to a FileDescriptor
Reviewed-by: chegar, bpb
This commit is contained in:
parent
7523687071
commit
cf9438f418
5 changed files with 575 additions and 1 deletions
|
@ -242,6 +242,7 @@ module java.base {
|
||||||
exports sun.nio.ch to
|
exports sun.nio.ch to
|
||||||
java.management,
|
java.management,
|
||||||
jdk.crypto.cryptoki,
|
jdk.crypto.cryptoki,
|
||||||
|
jdk.net,
|
||||||
jdk.sctp,
|
jdk.sctp,
|
||||||
jdk.unsupported;
|
jdk.unsupported;
|
||||||
exports sun.nio.cs to
|
exports sun.nio.cs to
|
||||||
|
|
263
src/jdk.net/share/classes/jdk/nio/Channels.java
Normal file
263
src/jdk.net/share/classes/jdk/nio/Channels.java
Normal file
|
@ -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}.
|
||||||
|
*
|
||||||
|
* <p> 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.
|
||||||
|
*
|
||||||
|
* <p> 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.
|
||||||
|
*
|
||||||
|
* <p> 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. </p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p> 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}.
|
||||||
|
*
|
||||||
|
* <p> 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}. </p>
|
||||||
|
*
|
||||||
|
* <p> 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. </p>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/jdk.net/share/classes/jdk/nio/package-info.java
Normal file
32
src/jdk.net/share/classes/jdk/nio/package-info.java
Normal file
|
@ -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;
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -31,5 +31,6 @@
|
||||||
*/
|
*/
|
||||||
module jdk.net {
|
module jdk.net {
|
||||||
exports jdk.net;
|
exports jdk.net;
|
||||||
|
exports jdk.nio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
277
test/jdk/jdk/nio/Basic.java
Normal file
277
test/jdk/jdk/nio/Basic.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue