1 /*
   2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  * @bug 7200742
  26  * @summary Test that Selector doesn't spin when changing interest ops
  27  */
  28 
  29 import java.net.*;
  30 import java.nio.ByteBuffer;
  31 import java.nio.channels.*;
  32 import static java.nio.channels.SelectionKey.*;
  33 import java.io.IOException;
  34 
  35 public class ChangingInterests {
  36 
  37     static int OPS[] = { 0, OP_WRITE, OP_READ, (OP_WRITE|OP_READ) };
  38 
  39     static String toOpsString(int ops) {
  40         String s = "";
  41         if ((ops & OP_READ) > 0)
  42             s += "POLLIN";
  43         if ((ops & OP_WRITE) > 0) {
  44             if (s.length() > 0)
  45                 s += "|";
  46             s += "POLLOUT";
  47         }
  48         if (s.length() == 0)
  49             s = "0";
  50         return "(" + s + ")";
  51     }
  52 
  53     static void write1(SocketChannel peer) throws IOException {
  54         peer.write(ByteBuffer.wrap(new byte[1]));
  55         // give time for other end to be readable
  56         try {
  57             Thread.sleep(1000);
  58         } catch (InterruptedException ignore) { }
  59     }
  60 
  61     static void drain(SocketChannel sc) throws IOException {
  62         ByteBuffer buf = ByteBuffer.allocate(100);
  63         int n;
  64         while ((n = sc.read(buf)) > 0) {
  65             buf.rewind();
  66         }
  67     }
  68 
  69     /**
  70      * Changes the given key's interest set from one set to another and then
  71      * checks the selected key set and the key's channel.
  72      */
  73     static void testChange(SelectionKey key, int from, int to) throws IOException {
  74         Selector sel = key.selector();
  75         assertTrue(sel.keys().size() == 1, "Only one channel should be registered");
  76 
  77         // ensure that channel is registered with the "from" interest set
  78         key.interestOps(from);
  79         sel.selectNow();
  80         sel.selectedKeys().clear();
  81 
  82         // change to the "to" interest set
  83         key.interestOps(to);
  84         System.out.println("select...");
  85         int selected = sel.selectNow();
  86         System.out.println("" + selected + " channel(s) selected");
  87 
  88         int expected = (to == 0) ? 0 : 1;
  89         assertTrue(selected == expected, "Expected " + expected);
  90 
  91         // check selected keys
  92         for (SelectionKey k: sel.selectedKeys()) {
  93             assertTrue(k == key, "Unexpected key selected");
  94 
  95             boolean readable = k.isReadable();
  96             boolean writable = k.isWritable();
  97 
  98             System.out.println("key readable: " + readable);
  99             System.out.println("key writable: " + writable);
 100 
 101             if ((to & OP_READ) == 0) {
 102                 assertTrue(!readable, "Not expected to be readable");
 103             } else {
 104                 assertTrue(readable, "Expected to be readable");
 105             }
 106 
 107             if ((to & OP_WRITE) == 0) {
 108                 assertTrue(!writable, "Not expected to be writable");
 109             } else {
 110                 assertTrue(writable, "Expected to be writable");
 111             }
 112 
 113             sel.selectedKeys().clear();
 114         }
 115     }
 116 
 117     /**
 118      * Tests that given Selector's select method blocks.
 119      */
 120     static void testForSpin(Selector sel) throws IOException {
 121         System.out.println("Test for spin...");
 122         long start = System.currentTimeMillis();
 123         int count = 3;
 124         while (count-- > 0) {
 125             int selected = sel.select(1000);
 126             System.out.println("" + selected + " channel(s) selected");
 127             assertTrue(selected == 0, "Channel should not be selected");
 128         }
 129         long dur = System.currentTimeMillis() - start;
 130         assertTrue(dur > 1000, "select was too short");
 131     }
 132 
 133     public static void main(String[] args) throws IOException {
 134         InetAddress lh = InetAddress.getLocalHost();
 135 
 136         // create loopback connection
 137         ServerSocketChannel ssc =
 138             ServerSocketChannel.open().bind(new InetSocketAddress(0));
 139 
 140         final SocketChannel sc = SocketChannel.open();
 141         sc.connect(new InetSocketAddress(lh, ssc.socket().getLocalPort()));
 142         SocketChannel peer = ssc.accept();
 143 
 144         sc.configureBlocking(false);
 145 
 146         // ensure that channel "sc" is readable
 147         write1(peer);
 148 
 149         try (Selector sel = Selector.open()) {
 150             SelectionKey key = sc.register(sel, 0);
 151             sel.selectNow();
 152 
 153             // test all transitions
 154             for (int from: OPS) {
 155                 for (int to: OPS) {
 156 
 157                     System.out.println(toOpsString(from) + " -> " + toOpsString(to));
 158 
 159                     testChange(key, from, to);
 160 
 161                     // if the interst ops is now 0 then Selector should not spin
 162                     if (to == 0)
 163                         testForSpin(sel);
 164 
 165                     // if interest ops is now OP_READ then make non-readable
 166                     // and test that Selector does not spin.
 167                     if (to == OP_READ) {
 168                         System.out.println("Drain channel...");
 169                         drain(sc);
 170                         testForSpin(sel);
 171                         System.out.println("Make channel readable again");
 172                         write1(peer);
 173                     }
 174 
 175                     System.out.println();
 176                 }
 177             }
 178 
 179         } finally {
 180             sc.close();
 181             peer.close();
 182             ssc.close();
 183         }
 184     }
 185 
 186     static void assertTrue(boolean v, String msg) {
 187         if (!v) throw new RuntimeException(msg);
 188     }
 189 
 190 }