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     }
  56 
  57     static void drain(SocketChannel sc) throws IOException {
  58         ByteBuffer buf = ByteBuffer.allocate(100);
  59         int n;
  60         while ((n = sc.read(buf)) > 0) {
  61             buf.rewind();
  62         }
  63     }
  64 
  65     /**
  66      * Changes the given key's interest set from one set to another and then
  67      * checks the selected key set and the key's channel.
  68      */
  69     static void testChange(SelectionKey key, int from, int to) throws IOException {
  70         Selector sel = key.selector();
  71         assertTrue(sel.keys().size() == 1, "Only one channel should be registered");
  72 
  73         // ensure that channel is registered with the "from" interest set
  74         key.interestOps(from);
  75         sel.selectNow();
  76         sel.selectedKeys().clear();
  77 
  78         // change to the "to" interest set
  79         key.interestOps(to);
  80 
  81         int readCounter = ((to & OP_READ) == 0) ? 0 : 1;
  82         int writeCounter = ((to & OP_WRITE) == 0) ? 0 : 1;
  83         System.out.printf("Expected number of readable events %d, writable events %d\n",
  84                 readCounter, writeCounter);
  85 
  86         long start = System.currentTimeMillis();
  87 
  88         while ((readCounter > 0 || writeCounter > 0) 
  89                 && System.currentTimeMillis() - start < 10000) {
  90             System.out.println("\ttry selection");
  91             try {
  92                 Thread.sleep(1000);
  93             } catch (InterruptedException ignore) {}
  94 
  95             sel.selectNow();
  96 
  97             // check selected keys
  98             for (SelectionKey k : sel.selectedKeys()) {
  99                 assertTrue(k == key, "Unexpected key selected");
 100 
 101                 if (k.isReadable()) {
 102                     readCounter--;
 103                 }
 104                 if (k.isWritable()) {
 105                     writeCounter--;
 106                 }
 107             }
 108         }
 109 
 110         verifyEvents(to, readCounter, writeCounter);
 111         sel.selectedKeys().clear();
 112     }
 113 
 114     /**
 115      * Verify actual arrived events with current interest configuration
 116      */
 117     static void verifyEvents(int op, int readCounter, int writeCounter) {
 118         System.out.printf("Verify : %s <-> readCounter %d writeCounter %d\n",
 119                 toOpsString(op), readCounter, writeCounter);
 120         switch (op) {
 121             case 0 :
 122                 assertTrue(readCounter == 0 && writeCounter == 0, 
 123                         "Expecting no event");
 124                 break;
 125             case OP_READ :
 126                 assertTrue(readCounter == 0, "Expecting only read event");
 127                 break;
 128             case OP_WRITE :
 129                 assertTrue(writeCounter == 0, "Expecting only write event");
 130                 break;
 131             case OP_READ | OP_WRITE :
 132                 assertTrue(readCounter == 0 && writeCounter <= 0, 
 133                         "Expecting read and write events");
 134                 break;
 135             default:
 136                 assertTrue(false, "Wrong interest setting");
 137                 break;
 138         }
 139     }
 140 
 141     /**
 142      * Tests that given Selector's select method blocks.
 143      */
 144     static void testForSpin(Selector sel) throws IOException {
 145         System.out.println("Test for spin...");
 146         long start = System.currentTimeMillis();
 147         int count = 3;
 148         while (count-- > 0) {
 149             int selected = sel.select(1000);
 150             System.out.println("" + selected + " channel(s) selected");
 151             assertTrue(selected == 0, "Channel should not be selected");
 152         }
 153         long dur = System.currentTimeMillis() - start;
 154         assertTrue(dur > 1000, "select was too short");
 155     }
 156 
 157     public static void main(String[] args) throws IOException {
 158         InetAddress lh = InetAddress.getLocalHost();
 159 
 160         // create loopback connection
 161         ServerSocketChannel ssc =
 162             ServerSocketChannel.open().bind(new InetSocketAddress(0));
 163 
 164         final SocketChannel sc = SocketChannel.open();
 165         sc.connect(new InetSocketAddress(lh, ssc.socket().getLocalPort()));
 166         SocketChannel peer = ssc.accept();
 167 
 168         sc.configureBlocking(false);
 169 
 170         // ensure that channel "sc" is readable
 171         write1(peer);
 172 
 173         try (Selector sel = Selector.open()) {
 174             SelectionKey key = sc.register(sel, 0);
 175             sel.selectNow();
 176 
 177             // test all transitions
 178             for (int from: OPS) {
 179                 for (int to: OPS) {
 180 
 181                     System.out.println(toOpsString(from) + " -> " + toOpsString(to));
 182 
 183                     testChange(key, from, to);
 184 
 185                     // if the interst ops is now 0 then Selector should not spin
 186                     if (to == 0)
 187                         testForSpin(sel);
 188 
 189                     // if interest ops is now OP_READ then make non-readable
 190                     // and test that Selector does not spin.
 191                     if (to == OP_READ) {
 192                         System.out.println("Drain channel...");
 193                         drain(sc);
 194                         testForSpin(sel);
 195                         System.out.println("Make channel readable again");
 196                         write1(peer);
 197                     }
 198 
 199                     System.out.println();
 200                 }
 201             }
 202 
 203         } finally {
 204             sc.close();
 205             peer.close();
 206             ssc.close();
 207         }
 208     }
 209 
 210     static void assertTrue(boolean v, String msg) {
 211         if (!v) throw new RuntimeException(msg);
 212     }
 213 
 214 }