7200742: (se) Selector.select does not block when starting Coherence (sol)
Reviewed-by: chegar
--- a/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java Wed Sep 26 21:05:40 2012 -0700
+++ b/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java Thu Sep 27 10:30:56 2012 +0100
@@ -68,7 +68,7 @@
static final short REVENT_OFFSET = 6;
// Special value to indicate that an update should be ignored
- static final byte CANCELLED = (byte)-1;
+ static final byte IGNORE = (byte)-1;
// Maximum number of open file descriptors
static final int OPEN_MAX = IOUtil.fdLimit();
@@ -192,15 +192,15 @@
// events are stored as bytes for efficiency reasons
byte b = (byte)mask;
- assert (b == mask) && (b != CANCELLED);
+ assert (b == mask) && (b != IGNORE);
setUpdateEvents(fd, b);
}
}
void release(int fd) {
synchronized (updateLock) {
- // cancel any pending update for this file descriptor
- setUpdateEvents(fd, CANCELLED);
+ // ignore any pending update for this file descriptor
+ setUpdateEvents(fd, IGNORE);
// remove from /dev/poll
if (registered.get(fd)) {
@@ -236,32 +236,40 @@
while (j < updateCount) {
int fd = updateDescriptors[j];
short events = getUpdateEvents(fd);
- boolean isRegistered = registered.get(fd);
+ boolean wasRegistered = registered.get(fd);
// events = 0 => POLLREMOVE or do-nothing
- if (events != CANCELLED) {
+ if (events != IGNORE) {
if (events == 0) {
- if (isRegistered) {
+ if (wasRegistered) {
events = POLLREMOVE;
registered.clear(fd);
} else {
- events = CANCELLED;
+ events = IGNORE;
}
} else {
- if (!isRegistered) {
+ if (!wasRegistered) {
registered.set(fd);
}
}
}
// populate pollfd array with updated event
- if (events != CANCELLED) {
+ if (events != IGNORE) {
+ // insert POLLREMOVE if changing events
+ if (wasRegistered && events != POLLREMOVE) {
+ putPollFD(pollArray, index, fd, POLLREMOVE);
+ index++;
+ }
putPollFD(pollArray, index, fd, events);
index++;
- if (index >= NUM_POLLFDS) {
+ if (index >= (NUM_POLLFDS-1)) {
registerMultiple(wfd, pollArray.address(), index);
index = 0;
}
+
+ // events for this fd now up to date
+ setUpdateEvents(fd, IGNORE);
}
j++;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Selector/ChangingInterests.java Thu Sep 27 10:30:56 2012 +0100
@@ -0,0 +1,190 @@
+/*
+ * 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]));
+ // give time for other end to be readable
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException ignore) { }
+ }
+
+ 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.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);
+ }
+
+}