/* * Copyright (c) 2012, 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 * @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 + ")"; } static void write1(SocketChannel peer) throws IOException { peer.write(ByteBuffer.wrap(new byte[1])); } 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); int readCounter = ((to & OP_READ) == 0) ? 0 : 1; int writeCounter = ((to & OP_WRITE) == 0) ? 0 : 1; System.out.printf("Expected number of readable events %d, writable events %d\n", readCounter, writeCounter); long start = System.currentTimeMillis(); while ((readCounter > 0 || writeCounter > 0) && System.currentTimeMillis() - start < 10000) { System.out.println("\ttry selection"); try { Thread.sleep(1000); } catch (InterruptedException ignore) {} sel.selectNow(); // check selected keys for (SelectionKey k : sel.selectedKeys()) { assertTrue(k == key, "Unexpected key selected"); if (k.isReadable()) { readCounter--; } if (k.isWritable()) { writeCounter--; } } } verifyEvents(to, readCounter, writeCounter); sel.selectedKeys().clear(); } /** * Verify actual arrived events with current interest configuration */ static void verifyEvents(int op, int readCounter, int writeCounter) { System.out.printf("Verify : %s <-> readCounter %d writeCounter %d\n", toOpsString(op), readCounter, writeCounter); switch (op) { case 0 : assertTrue(readCounter == 0 && writeCounter == 0, "Expecting no event"); break; case OP_READ : assertTrue(readCounter == 0, "Expecting only read event"); break; case OP_WRITE : assertTrue(writeCounter == 0, "Expecting only write event"); break; case OP_READ | OP_WRITE : assertTrue(readCounter == 0 && writeCounter <= 0, "Expecting read and write events"); break; default: assertTrue(false, "Wrong interest setting"); break; } } /** * 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.connect(new InetSocketAddress(lh, ssc.socket().getLocalPort())); SocketChannel peer = ssc.accept(); sc.configureBlocking(false); // ensure that channel "sc" is readable write1(peer); 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"); write1(peer); } System.out.println(); } } } finally { sc.close(); peer.close(); ssc.close(); } } static void assertTrue(boolean v, String msg) { if (!v) throw new RuntimeException(msg); } }