/* * Copyright 2009 Google Inc. 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. */ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Reproduces O(N^2) behavior of JDK6/7 select() call. This happens when * a selector has many unprocessed updates to its interest set (e.g. adding * OP_READ on a bunch of newly accepted sockets). The O(N^2) is triggered * by cancelling a number of selection keys (or just closing a few sockets). * In this case, select() will first go through the list of cancelled keys * and try to deregister them. That deregistration is O(N^2) over the list * of unprocessed updates to the interest set. * *
This O(N^2) behavior is a BUG in JVM and should be fixed. * *
The test first creates initCount connections, and adds them * to the server epoll set. It then creates massCount connections, * registers interest (causing updateList to be populated with massCount*2 * elements), but does not add them to epoll set (that would've cleared * updateList). The test then closes initCount connections, thus populating * deregistration queue. The subsequent call to selectNow() will first process * deregistration queue, performing O(N^2) over updateList size, * equal to massCount * 2. * *
Note that connect rate is artificially slowed down to compensate
* for what I believe is a Linux bug, where too high of a connection rate
* ends up in SYN's being dropped and then slow retransmits.
*
* @author Igor Chernyshev
*/
public class LotsOfCancels {
static long testStartTime;
public static void main(String[] args) throws Exception {
// the final select should run in less than 1000ms.
runTest(500, 2700, 1000);
}
static void log(String msg) {
System.out.println(getLogPrefix() + msg);
}
static String getLogPrefix() {
return durationMillis(testStartTime) + ": ";
}
/**
* Returns the elapsed time since startNanos, in milliseconds.
* @param startNanos the start time; this must be a value returned
* by {@link System.nanoTime}
*/
static long durationMillis(long startNanos) {
return (System.nanoTime() - startNanos) / (1000L * 1000L);
}
static void runTest(int initCount, int massCount, int maxSelectTime)
throws Exception {
testStartTime = System.nanoTime();
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7359);
// Create server channel, add it to selector and run epoll_ctl.
log("Setting up server");
Selector serverSelector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(address, 5000);
server.register(serverSelector, SelectionKey.OP_ACCEPT);
serverSelector.selectNow();
log("Setting up client");
ClientThread client = new ClientThread(address);
client.start();
Thread.sleep(100);
// Set up initial set of client sockets.
log("Starting initial client connections");
client.connectClients(initCount);
Thread.sleep(500); // Wait for client connections to arrive
// Accept all initial client sockets, add to selector and run
// epoll_ctl.
log("Accepting initial connections");
List