/*
* Copyright 2009 Google Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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.
*
* <p> This O(N^2) behavior is a BUG in JVM and should be fixed.
*
* <p> 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.
*
* <p> 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;
// the final select should run in less than 1000ms.
}
}
}
/**
* Returns the elapsed time since startNanos, in milliseconds.
* @param startNanos the start time; this must be a value returned
* by {@link System.nanoTime}
*/
}
throws Exception {
// Create server channel, add it to selector and run epoll_ctl.
log("Setting up server");
server.configureBlocking(false);
log("Setting up client");
// Set up initial set of client sockets.
log("Starting initial client connections");
// Accept all initial client sockets, add to selector and run
// epoll_ctl.
log("Accepting initial connections");
" instead of " + initCount);
}
// Set up mass set of client sockets.
log("Requesting mass client connections");
// Accept all mass client sockets, add to selector and do NOT
// run epoll_ctl.
log("Accepting mass connections");
" instead of " + massCount);
}
// Close initial set of sockets.
log("Closing initial connections");
// Now get the timing of select() call.
log("Running the final select call");
", mass count = " + massCount +
if (duration > maxSelectTime) {
("\n\n\n\n\nFAILURE: The final selectNow() took " +
duration + "ms " +
"- seems like O(N^2) bug is still here\n\n");
}
}
int expected)
throws Exception {
int retryCount = 0;
int acceptCount = 0;
log("accept() returned null " +
acceptCount = 0;
if (retryCount < 10) {
// See if more new sockets got stacked behind.
retryCount++;
continue;
}
break;
}
retryCount = 0;
acceptCount++;
channel.configureBlocking(false);
}
// Cause an additional updateList entry per channel.
}
return channels;
}
throws Exception {
}
}
private int connectionsNeeded;
private int totalCreated;
setDaemon(true);
}
synchronized (this) {
}
}
public void run() {
try {
} catch (Throwable e) {
e.printStackTrace();
}
}
int selectCount = 0;
while (true) {
int createdCount = 0;
synchronized (this) {
if (connectionsNeeded > 0) {
createdCount++;
totalCreated++;
channel.configureBlocking(false);
if (!channel.finishConnect()) {
}
}
log("Started total of " +
totalCreated + " client connections");
}
}
if (createdCount > 0) {
} else {
selectCount++;
log("Exited clientSelector.select(), loop #"
}
int keyCount = -1;
synchronized (key) {
keyCount++;
continue;
}
} else {
" returned " + readyOps);
}
}
}
}
}
}
}