test/jdk/java/nio/channels/Selector/ChangingInterests.java
changeset 47216 71c04702a3d5
parent 44120 f569cf365ae3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/Selector/ChangingInterests.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2012, 2017, 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 7200742
+ * @key intermittent
+ * @summary Test that Selector doesn't spin when changing interest ops
+ */
+
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import static java.nio.channels.SelectionKey.*;
+import java.io.IOException;
+
+public class ChangingInterests {
+
+    static int OPS[] = { 0, OP_WRITE, OP_READ, (OP_WRITE|OP_READ) };
+
+    static String toOpsString(int ops) {
+        String s = "";
+        if ((ops & OP_READ) > 0)
+            s += "POLLIN";
+        if ((ops & OP_WRITE) > 0) {
+            if (s.length() > 0)
+                s += "|";
+            s += "POLLOUT";
+        }
+        if (s.length() == 0)
+            s = "0";
+        return "(" + s + ")";
+    }
+
+    /**
+     * Writes two bytes to 'out' and reads one byte from 'in' so
+     * as to make 'in' readable.
+     */
+    static void makeReadable(SocketChannel out, SocketChannel in) throws IOException {
+        out.write(ByteBuffer.wrap(new byte[2]));
+        ByteBuffer oneByte = ByteBuffer.wrap(new byte[1]);
+        do {
+            int n = in.read(oneByte);
+            if (n == 1) {
+                break;
+            } else if (n == 0) {
+                try {
+                    Thread.sleep(50);
+                } catch (InterruptedException ignore) { }
+            } else {
+                throw new RuntimeException
+                    ("Expected to read 0 or 1 byte; actual number was " + n);
+            }
+        } while (true);
+    }
+
+    static void drain(SocketChannel sc) throws IOException {
+        ByteBuffer buf = ByteBuffer.allocate(100);
+        int n;
+        while ((n = sc.read(buf)) > 0) {
+            buf.rewind();
+        }
+    }
+
+    /**
+     * Changes the given key's interest set from one set to another and then
+     * checks the selected key set and the key's channel.
+     */
+    static void testChange(SelectionKey key, int from, int to) throws IOException {
+        Selector sel = key.selector();
+        assertTrue(sel.keys().size() == 1, "Only one channel should be registered");
+
+        // ensure that channel is registered with the "from" interest set
+        key.interestOps(from);
+        sel.selectNow();
+        sel.selectedKeys().clear();
+
+        // change to the "to" interest set
+        key.interestOps(to);
+        System.out.println("select...");
+        int selected = sel.selectNow();
+        System.out.println("" + selected + " channel(s) selected");
+
+        int expected = (to == 0) ? 0 : 1;
+        assertTrue(selected == expected, "Expected " + expected);
+
+        // check selected keys
+        for (SelectionKey k: sel.selectedKeys()) {
+            assertTrue(k == key, "Unexpected key selected");
+
+            boolean readable = k.isReadable();
+            boolean writable = k.isWritable();
+
+            System.out.println("key readable: " + readable);
+            System.out.println("key writable: " + writable);
+
+            if ((to & OP_READ) == 0) {
+                assertTrue(!readable, "Not expected to be readable");
+            } else {
+                assertTrue(readable, "Expected to be readable");
+            }
+
+            if ((to & OP_WRITE) == 0) {
+                assertTrue(!writable, "Not expected to be writable");
+            } else {
+                assertTrue(writable, "Expected to be writable");
+            }
+
+            sel.selectedKeys().clear();
+        }
+    }
+
+    /**
+     * Tests that given Selector's select method blocks.
+     */
+    static void testForSpin(Selector sel) throws IOException {
+        System.out.println("Test for spin...");
+        long start = System.currentTimeMillis();
+        int count = 3;
+        while (count-- > 0) {
+            int selected = sel.select(1000);
+            System.out.println("" + selected + " channel(s) selected");
+            assertTrue(selected == 0, "Channel should not be selected");
+        }
+        long dur = System.currentTimeMillis() - start;
+        assertTrue(dur > 1000, "select was too short");
+    }
+
+    public static void main(String[] args) throws IOException {
+        InetAddress lh = InetAddress.getLocalHost();
+
+        // create loopback connection
+        ServerSocketChannel ssc =
+            ServerSocketChannel.open().bind(new InetSocketAddress(0));
+
+        final SocketChannel sc = SocketChannel.open();
+        sc.setOption(StandardSocketOptions.TCP_NODELAY, true);
+        sc.connect(new InetSocketAddress(lh, ssc.socket().getLocalPort()));
+        SocketChannel peer = ssc.accept();
+        peer.setOption(StandardSocketOptions.TCP_NODELAY, true);
+
+        sc.configureBlocking(false);
+
+        // ensure that channel "sc" is readable
+        makeReadable(peer, sc);
+
+        try (Selector sel = Selector.open()) {
+            SelectionKey key = sc.register(sel, 0);
+            sel.selectNow();
+
+            // test all transitions
+            for (int from: OPS) {
+                for (int to: OPS) {
+
+                    System.out.println(toOpsString(from) + " -> " + toOpsString(to));
+
+                    testChange(key, from, to);
+
+                    // if the interst ops is now 0 then Selector should not spin
+                    if (to == 0)
+                        testForSpin(sel);
+
+                    // if interest ops is now OP_READ then make non-readable
+                    // and test that Selector does not spin.
+                    if (to == OP_READ) {
+                        System.out.println("Drain channel...");
+                        drain(sc);
+                        testForSpin(sel);
+                        System.out.println("Make channel readable again");
+                        makeReadable(peer, sc);
+                    }
+
+                    System.out.println();
+                }
+            }
+
+        } finally {
+            sc.close();
+            peer.close();
+            ssc.close();
+        }
+    }
+
+    static void assertTrue(boolean v, String msg) {
+        if (!v) throw new RuntimeException(msg);
+    }
+
+}