Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/nio/channels/Selector/LotsOfCancels.java
38828 views
/*1* Copyright 2009 Google Inc. All Rights Reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.net.InetSocketAddress;24import java.net.SocketAddress;25import java.nio.channels.SelectionKey;26import java.nio.channels.Selector;27import java.nio.channels.ServerSocketChannel;28import java.nio.channels.SocketChannel;29import java.util.ArrayList;30import java.util.Iterator;31import java.util.List;3233/**34* Reproduces O(N^2) behavior of JDK6/7 select() call. This happens when35* a selector has many unprocessed updates to its interest set (e.g. adding36* OP_READ on a bunch of newly accepted sockets). The O(N^2) is triggered37* by cancelling a number of selection keys (or just closing a few sockets).38* In this case, select() will first go through the list of cancelled keys39* and try to deregister them. That deregistration is O(N^2) over the list40* of unprocessed updates to the interest set.41*42* <p> This O(N^2) behavior is a BUG in JVM and should be fixed.43*44* <p> The test first creates initCount connections, and adds them45* to the server epoll set. It then creates massCount connections,46* registers interest (causing updateList to be populated with massCount*247* elements), but does not add them to epoll set (that would've cleared48* updateList). The test then closes initCount connections, thus populating49* deregistration queue. The subsequent call to selectNow() will first process50* deregistration queue, performing O(N^2) over updateList size,51* equal to massCount * 2.52*53* <p> Note that connect rate is artificially slowed down to compensate54* for what I believe is a Linux bug, where too high of a connection rate55* ends up in SYN's being dropped and then slow retransmits.56*57* @author Igor Chernyshev58*/59public class LotsOfCancels {6061static long testStartTime;6263public static void main(String[] args) throws Exception {64// the final select should run in less than 1000ms.65runTest(500, 2700, 1000);66}6768static void log(String msg) {69System.out.println(getLogPrefix() + msg);70}7172static String getLogPrefix() {73return durationMillis(testStartTime) + ": ";74}7576/**77* Returns the elapsed time since startNanos, in milliseconds.78* @param startNanos the start time; this must be a value returned79* by {@link System.nanoTime}80*/81static long durationMillis(long startNanos) {82return (System.nanoTime() - startNanos) / (1000L * 1000L);83}8485static void runTest(int initCount, int massCount, int maxSelectTime)86throws Exception {87testStartTime = System.nanoTime();8889InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7359);9091// Create server channel, add it to selector and run epoll_ctl.92log("Setting up server");93Selector serverSelector = Selector.open();94ServerSocketChannel server = ServerSocketChannel.open();95server.configureBlocking(false);96server.socket().bind(address, 5000);97server.register(serverSelector, SelectionKey.OP_ACCEPT);98serverSelector.selectNow();99100log("Setting up client");101ClientThread client = new ClientThread(address);102client.start();103Thread.sleep(100);104105// Set up initial set of client sockets.106log("Starting initial client connections");107client.connectClients(initCount);108Thread.sleep(500); // Wait for client connections to arrive109110// Accept all initial client sockets, add to selector and run111// epoll_ctl.112log("Accepting initial connections");113List<SocketChannel> serverChannels1 =114acceptAndAddAll(serverSelector, server, initCount);115if (serverChannels1.size() != initCount) {116throw new Exception("Accepted " + serverChannels1.size() +117" instead of " + initCount);118}119serverSelector.selectNow();120121// Set up mass set of client sockets.122log("Requesting mass client connections");123client.connectClients(massCount);124Thread.sleep(500); // Wait for client connections to arrive125126// Accept all mass client sockets, add to selector and do NOT127// run epoll_ctl.128log("Accepting mass connections");129List<SocketChannel> serverChannels2 =130acceptAndAddAll(serverSelector, server, massCount);131if (serverChannels2.size() != massCount) {132throw new Exception("Accepted " + serverChannels2.size() +133" instead of " + massCount);134}135136// Close initial set of sockets.137log("Closing initial connections");138closeAll(serverChannels1);139140// Now get the timing of select() call.141log("Running the final select call");142long startTime = System.nanoTime();143serverSelector.selectNow();144long duration = durationMillis(startTime);145log("Init count = " + initCount +146", mass count = " + massCount +147", duration = " + duration + "ms");148149if (duration > maxSelectTime) {150System.out.println151("\n\n\n\n\nFAILURE: The final selectNow() took " +152duration + "ms " +153"- seems like O(N^2) bug is still here\n\n");154System.exit(1);155}156}157158static List<SocketChannel> acceptAndAddAll(Selector selector,159ServerSocketChannel server,160int expected)161throws Exception {162int retryCount = 0;163int acceptCount = 0;164List<SocketChannel> channels = new ArrayList<SocketChannel>();165while (channels.size() < expected) {166SocketChannel channel = server.accept();167if (channel == null) {168log("accept() returned null " +169"after accepting " + acceptCount + " more connections");170acceptCount = 0;171if (retryCount < 10) {172// See if more new sockets got stacked behind.173retryCount++;174Thread.sleep(500);175continue;176}177break;178}179retryCount = 0;180acceptCount++;181channel.configureBlocking(false);182channel.register(selector, SelectionKey.OP_READ);183channels.add(channel);184}185// Cause an additional updateList entry per channel.186for (SocketChannel channel : channels) {187channel.register(selector, SelectionKey.OP_WRITE);188}189return channels;190}191192static void closeAll(List<SocketChannel> channels)193throws Exception {194for (SocketChannel channel : channels) {195channel.close();196}197}198199static class ClientThread extends Thread {200private final SocketAddress address;201private final Selector selector;202private int connectionsNeeded;203private int totalCreated;204205ClientThread(SocketAddress address) throws Exception {206this.address = address;207selector = Selector.open();208setDaemon(true);209}210211void connectClients(int count) throws Exception {212synchronized (this) {213connectionsNeeded += count;214}215selector.wakeup();216}217218@Override219public void run() {220try {221handleClients();222} catch (Throwable e) {223e.printStackTrace();224System.exit(1);225}226}227228private void handleClients() throws Exception {229int selectCount = 0;230while (true) {231int createdCount = 0;232synchronized (this) {233if (connectionsNeeded > 0) {234235while (connectionsNeeded > 0 && createdCount < 20) {236connectionsNeeded--;237createdCount++;238totalCreated++;239240SocketChannel channel = SocketChannel.open();241channel.configureBlocking(false);242channel.connect(address);243if (!channel.finishConnect()) {244channel.register(selector,245SelectionKey.OP_CONNECT);246}247}248249log("Started total of " +250totalCreated + " client connections");251Thread.sleep(200);252}253}254255if (createdCount > 0) {256selector.selectNow();257} else {258selectCount++;259long startTime = System.nanoTime();260selector.select();261long duration = durationMillis(startTime);262log("Exited clientSelector.select(), loop #"263+ selectCount + ", duration = " + duration + "ms");264}265266int keyCount = -1;267Iterator<SelectionKey> keys =268selector.selectedKeys().iterator();269while (keys.hasNext()) {270SelectionKey key = keys.next();271synchronized (key) {272keyCount++;273keys.remove();274if (!key.isValid()) {275log("Ignoring client key #" + keyCount);276continue;277}278int readyOps = key.readyOps();279if (readyOps == SelectionKey.OP_CONNECT) {280key.interestOps(0);281((SocketChannel) key.channel()).finishConnect();282} else {283log("readyOps() on client key #" + keyCount +284" returned " + readyOps);285}286}287}288}289}290}291}292293294