8200458: (se) Readiness information previously recorded in the ready set not preserved
Reviewed-by: bpb, chegar
--- a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java Thu Apr 05 15:01:57 2018 +0100
+++ b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java Thu Apr 05 15:04:09 2018 +0100
@@ -191,7 +191,7 @@
if (ski != null) {
int rOps = EPoll.getEvents(event);
if (selectedKeys.contains(ski)) {
- if (ski.translateAndSetReadyOps(rOps)) {
+ if (ski.translateAndUpdateReadyOps(rOps)) {
numKeysUpdated++;
}
} else {
--- a/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java Thu Apr 05 15:01:57 2018 +0100
+++ b/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java Thu Apr 05 15:04:09 2018 +0100
@@ -216,15 +216,12 @@
}
if (selectedKeys.contains(ski)) {
- // file descriptor may be polled more than once per poll
- if (ski.lastPolled != pollCount) {
- if (ski.translateAndSetReadyOps(rOps)) {
+ if (ski.translateAndUpdateReadyOps(rOps)) {
+ // file descriptor may be polled more than once per poll
+ if (ski.lastPolled != pollCount) {
numKeysUpdated++;
ski.lastPolled = pollCount;
}
- } else {
- // ready ops have already been set on this update
- ski.translateAndUpdateReadyOps(rOps);
}
} else {
ski.translateAndSetReadyOps(rOps);
--- a/src/java.base/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java Thu Apr 05 15:01:57 2018 +0100
+++ b/src/java.base/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java Thu Apr 05 15:04:09 2018 +0100
@@ -184,7 +184,7 @@
if (ski != null) {
int rOps = pollWrapper.getReventOps(i);
if (selectedKeys.contains(ski)) {
- if (ski.translateAndSetReadyOps(rOps)) {
+ if (ski.translateAndUpdateReadyOps(rOps)) {
numKeysUpdated++;
}
} else {
--- a/src/java.base/solaris/classes/sun/nio/ch/EventPortSelectorImpl.java Thu Apr 05 15:01:57 2018 +0100
+++ b/src/java.base/solaris/classes/sun/nio/ch/EventPortSelectorImpl.java Thu Apr 05 15:04:09 2018 +0100
@@ -188,7 +188,7 @@
if (ski != null) {
int rOps = getEventOps(i);
if (selectedKeys.contains(ski)) {
- if (ski.translateAndSetReadyOps(rOps)) {
+ if (ski.translateAndUpdateReadyOps(rOps)) {
numKeysUpdated++;
}
} else {
--- a/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java Thu Apr 05 15:01:57 2018 +0100
+++ b/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java Thu Apr 05 15:04:09 2018 +0100
@@ -174,7 +174,7 @@
assert ski.getFDVal() == getDescriptor(i);
if (ski.isValid()) {
if (selectedKeys.contains(ski)) {
- if (ski.translateAndSetReadyOps(rOps)) {
+ if (ski.translateAndUpdateReadyOps(rOps)) {
numKeysUpdated++;
}
} else {
--- a/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java Thu Apr 05 15:01:57 2018 +0100
+++ b/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java Thu Apr 05 15:04:09 2018 +0100
@@ -98,7 +98,6 @@
private static final class MapEntry {
final SelectionKeyImpl ski;
long updateCount = 0;
- long clearedCount = 0;
MapEntry(SelectionKeyImpl ski) {
this.ski = ski;
}
@@ -368,12 +367,10 @@
}
/**
- * Note, clearedCount is used to determine if the readyOps have
- * been reset in this select operation. updateCount is used to
- * tell if a key has been counted as updated in this select
- * operation.
+ * updateCount is used to tell if a key has been counted as updated
+ * in this select operation.
*
- * me.updateCount <= me.clearedCount <= updateCount
+ * me.updateCount <= updateCount
*/
private int processFDSet(long updateCount, int[] fds, int rOps,
boolean isExceptFds)
@@ -405,37 +402,19 @@
}
if (selectedKeys.contains(sk)) { // Key in selected set
- if (me.clearedCount != updateCount) {
- if (sk.translateAndSetReadyOps(rOps) &&
- (me.updateCount != updateCount)) {
- me.updateCount = updateCount;
- numKeysUpdated++;
- }
- } else { // The readyOps have been set; now add
- if (sk.translateAndUpdateReadyOps(rOps) &&
- (me.updateCount != updateCount)) {
+ if (sk.translateAndUpdateReadyOps(rOps)) {
+ if (me.updateCount != updateCount) {
me.updateCount = updateCount;
numKeysUpdated++;
}
}
- me.clearedCount = updateCount;
} else { // Key is not in selected set yet
- if (me.clearedCount != updateCount) {
- sk.translateAndSetReadyOps(rOps);
- if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) {
- selectedKeys.add(sk);
- me.updateCount = updateCount;
- numKeysUpdated++;
- }
- } else { // The readyOps have been set; now add
- sk.translateAndUpdateReadyOps(rOps);
- if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) {
- selectedKeys.add(sk);
- me.updateCount = updateCount;
- numKeysUpdated++;
- }
+ sk.translateAndSetReadyOps(rOps);
+ if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) {
+ selectedKeys.add(sk);
+ me.updateCount = updateCount;
+ numKeysUpdated++;
}
- me.clearedCount = updateCount;
}
}
return numKeysUpdated;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/Selector/UpdateReadyOps.java Thu Apr 05 15:04:09 2018 +0100
@@ -0,0 +1,176 @@
+/*
+ * 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
+ * @run testng UpdateReadyOps
+ * @summary Test that the ready set from a selection operation is bitwise-disjoined
+ * into a key's ready set when the key is already in the selected-key set
+ */
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class UpdateReadyOps {
+
+ /**
+ * Test that OP_WRITE is preserved when updating the ready set of a key in
+ * the selected-key set to add OP_READ.
+ */
+ public void testOpWritePreserved() throws Exception {
+ try (ConnectionPair pair = new ConnectionPair();
+ Selector sel = Selector.open()) {
+
+ SocketChannel sc1 = pair.channel1();
+ SocketChannel sc2 = pair.channel2();
+
+ sc1.configureBlocking(false);
+ SelectionKey key = sc1.register(sel, SelectionKey.OP_WRITE);
+
+ int updated = sel.select();
+ assertTrue(updated == 1);
+ assertTrue(sel.selectedKeys().contains(key));
+ assertFalse(key.isReadable());
+ assertTrue(key.isWritable());
+
+ // select again, should be no updates
+ updated = sel.select();
+ assertTrue(updated == 0);
+ assertTrue(sel.selectedKeys().contains(key));
+ assertFalse(key.isReadable());
+ assertTrue(key.isWritable());
+
+ // write some bytes
+ sc2.write(helloMessage());
+
+ // change interest ops to OP_READ, do a selection operation, and
+ // check that the ready set becomes OP_READ|OP_WRITE.
+
+ key.interestOps(SelectionKey.OP_READ);
+ updated = sel.select();
+ assertTrue(updated == 1);
+ assertTrue(sel.selectedKeys().size() == 1);
+ assertTrue(key.isReadable());
+ assertTrue(key.isWritable());
+ assertTrue(key.readyOps() == (SelectionKey.OP_READ|SelectionKey.OP_WRITE));
+
+ // select again, should be no updates
+ updated = sel.select();
+ assertTrue(updated == 0);
+ assertTrue(sel.selectedKeys().size() == 1);
+ assertTrue(key.isReadable());
+ assertTrue(key.isWritable());
+ }
+ }
+
+ /**
+ * Test that OP_READ is preserved when updating the ready set of a key in
+ * the selected-key set to add OP_WRITE.
+ */
+ public void testOpReadPreserved() throws Exception {
+ try (ConnectionPair pair = new ConnectionPair();
+ Selector sel = Selector.open()) {
+
+ SocketChannel sc1 = pair.channel1();
+ SocketChannel sc2 = pair.channel2();
+
+ sc1.configureBlocking(false);
+ SelectionKey key = sc1.register(sel, SelectionKey.OP_READ);
+
+ // write some bytes
+ sc2.write(helloMessage());
+
+ int updated = sel.select();
+ assertTrue(updated == 1);
+ assertTrue(sel.selectedKeys().size() == 1);
+ assertTrue(sel.selectedKeys().contains(key));
+ assertTrue(key.isReadable());
+ assertFalse(key.isWritable());
+
+ // select again, should be no updates
+ updated = sel.select();
+ assertTrue(updated == 0);
+ assertTrue(sel.selectedKeys().contains(key));
+ assertTrue(key.isReadable());
+ assertFalse(key.isWritable());
+
+ key.interestOps(SelectionKey.OP_WRITE);
+ updated = sel.select();
+ assertTrue(updated == 1);
+ assertTrue(sel.selectedKeys().size() == 1);
+ assertTrue(sel.selectedKeys().contains(key));
+ assertTrue(key.isReadable());
+ assertTrue(key.isWritable());
+ assertTrue(key.readyOps() == (SelectionKey.OP_READ|SelectionKey.OP_WRITE));
+
+ // select again, should be no updates
+ updated = sel.select();
+ assertTrue(updated == 0);
+ assertTrue(sel.selectedKeys().size() == 1);
+ assertTrue(key.isReadable());
+ assertTrue(key.isWritable());
+ }
+ }
+
+ static class ConnectionPair implements Closeable {
+
+ private final SocketChannel sc1;
+ private final SocketChannel sc2;
+
+ ConnectionPair() throws IOException {
+ InetAddress lb = InetAddress.getLoopbackAddress();
+ try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
+ ssc.bind(new InetSocketAddress(lb, 0));
+ this.sc1 = SocketChannel.open(ssc.getLocalAddress());
+ this.sc2 = ssc.accept();
+ }
+ }
+
+ SocketChannel channel1() {
+ return sc1;
+ }
+
+ SocketChannel channel2() {
+ return sc2;
+ }
+
+ public void close() throws IOException {
+ sc1.close();
+ sc2.close();
+ }
+ }
+
+ static ByteBuffer helloMessage() throws Exception {
+ return ByteBuffer.wrap("hello".getBytes("UTF-8"));
+ }
+}
--- a/test/jdk/java/nio/channels/SocketChannel/IsConnectable.java Thu Apr 05 15:01:57 2018 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2002, 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 4737146 4750573
- * @summary Test if isConnectable returns true after connected
- * @library .. /test/lib
- * @build jdk.test.lib.Utils TestServers
- * @run main IsConnectable
- */
-
-import java.net.*;
-import java.nio.channels.*;
-import java.nio.channels.spi.SelectorProvider;
-import java.util.*;
-
-public class IsConnectable {
-
- static void test(TestServers.DayTimeServer daytimeServer) throws Exception {
- InetSocketAddress isa
- = new InetSocketAddress(daytimeServer.getAddress(),
- daytimeServer.getPort());
- SocketChannel sc = SocketChannel.open();
- sc.configureBlocking(false);
- final boolean immediatelyConnected = sc.connect(isa);
-
- Selector selector = SelectorProvider.provider().openSelector();
- try {
- SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT);
- int keysAdded = selector.select();
- if (keysAdded > 0) {
- boolean result = sc.finishConnect();
- if (result) {
- keysAdded = selector.select(5000);
- // 4750573: keysAdded should not be incremented when op is dropped
- // from a key already in the selected key set
- if (keysAdded > 0)
- throw new Exception("Test failed: 4750573 detected");
- Set<SelectionKey> sel = selector.selectedKeys();
- Iterator<SelectionKey> i = sel.iterator();
- SelectionKey sk = i.next();
- // 4737146: isConnectable should be false while connected
- if (sk.isConnectable())
- throw new Exception("Test failed: 4737146 detected");
- }
- } else {
- if (!immediatelyConnected) {
- throw new Exception("Select failed");
- } else {
- System.out.println("IsConnectable couldn't be fully tested for "
- + System.getProperty("os.name"));
- }
- }
- } finally {
- sc.close();
- selector.close();
- }
- }
-
- public static void main(String[] args) throws Exception {
- try (TestServers.DayTimeServer daytimeServer
- = TestServers.DayTimeServer.startNewServer(100)) {
- test(daytimeServer);
- }
- }
-
-}