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 
toOpsString(int ops)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 
write1(SocketChannel peer)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(50);
58         } catch (InterruptedException ignore) { }
59     }
60 
drain(SocketChannel sc)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      */
testChange(SelectionKey key, int from, int to)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      */
testForSpin(Selector sel)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 
main(String[] args)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.setOption(StandardSocketOptions.TCP_NODELAY, true);
142         sc.connect(new InetSocketAddress(lh, ssc.socket().getLocalPort()));
143         SocketChannel peer = ssc.accept();
144         peer.setOption(StandardSocketOptions.TCP_NODELAY, true);
145 
146         sc.configureBlocking(false);
147 
148         // ensure that channel "sc" is readable
149         write1(peer);
150 
151         try (Selector sel = Selector.open()) {
152             SelectionKey key = sc.register(sel, 0);
153             sel.selectNow();
154 
155             // test all transitions
156             for (int from: OPS) {
157                 for (int to: OPS) {
158 
159                     System.out.println(toOpsString(from) + " -> " + toOpsString(to));
160 
161                     testChange(key, from, to);
162 
163                     // if the interst ops is now 0 then Selector should not spin
164                     if (to == 0)
165                         testForSpin(sel);
166 
167                     // if interest ops is now OP_READ then make non-readable
168                     // and test that Selector does not spin.
169                     if (to == OP_READ) {
170                         System.out.println("Drain channel...");
171                         drain(sc);
172                         testForSpin(sel);
173                         System.out.println("Make channel readable again");
174                         write1(peer);
175                     }
176 
177                     System.out.println();
178                 }
179             }
180 
181         } finally {
182             sc.close();
183             peer.close();
184             ssc.close();
185         }
186     }
187 
assertTrue(boolean v, String msg)188     static void assertTrue(boolean v, String msg) {
189         if (!v) throw new RuntimeException(msg);
190     }
191 
192 }
193