8200458: (se) Readiness information previously recorded in the ready set not preserved
authoralanb
Thu, 05 Apr 2018 15:04:09 +0100
changeset 49527 5aa40f834b50
parent 49526 cad4c844902a
child 49528 c1eb35eb5f38
8200458: (se) Readiness information previously recorded in the ready set not preserved Reviewed-by: bpb, chegar
src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java
src/java.base/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java
src/java.base/solaris/classes/sun/nio/ch/EventPortSelectorImpl.java
src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java
src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java
test/jdk/java/nio/channels/Selector/UpdateReadyOps.java
test/jdk/java/nio/channels/SocketChannel/IsConnectable.java
--- 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);
-        }
-    }
-
-}