diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index aee2a71524a..4b9d708c980 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -29,6 +29,7 @@ import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -350,29 +351,39 @@ public class Server extends HandlerWrapper implements Attributes } HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION); - MultiException mex=new MultiException(); + // check size of thread pool SizedThreadPool pool = getBean(SizedThreadPool.class); int max=pool==null?-1:pool.getMaxThreads(); int selectors=0; int acceptors=0; - if (mex.size()==0) - { - for (Connector connector : _connectors) - { - if (connector instanceof AbstractConnector) - acceptors+=((AbstractConnector)connector).getAcceptors(); - if (connector instanceof ServerConnector) - selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount(); - } + for (Connector connector : _connectors) + { + if (!(connector instanceof AbstractConnector)) + continue; + + AbstractConnector abstractConnector = (AbstractConnector) connector; + Executor connectorExecutor = connector.getExecutor(); + + if (connectorExecutor != pool) + // Do not count the selectors and acceptors from this connector at server level, because connector uses dedicated executor. + continue; + + acceptors += abstractConnector.getAcceptors(); + + if (connector instanceof ServerConnector) + selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount(); + } + int needed=1+selectors+acceptors; if (max>0 && needed>max) throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors)); + MultiException mex=new MultiException(); try { super.doStart(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/InsufficientThreadsDetectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/InsufficientThreadsDetectionTest.java new file mode 100644 index 00000000000..f54f7093ebd --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/InsufficientThreadsDetectionTest.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.junit.After; +import org.junit.Test; + +public class InsufficientThreadsDetectionTest { + + private Server _server; + + @After + public void dispose() throws Exception + { + _server.stop(); + } + + @Test(expected = IllegalStateException.class) + public void testConnectorUsesServerExecutorWithNotEnoughThreads() throws Exception + { + // server has 3 threads in the executor + _server = new Server(new QueuedThreadPool(3)); + + // connector will use executor from server because connectorPool is null + ThreadPool connectorPool = null; + // connector requires 7 threads(2 + 4 + 1) + ServerConnector connector = new ServerConnector(_server, connectorPool, null, null, 2, 4, new HttpConnectionFactory()); + connector.setPort(0); + _server.addConnector(connector); + + // should throw IllegalStateException because there are no required threads in server pool + _server.start(); + } + + @Test + public void testConnectorWithDedicatedExecutor() throws Exception + { + // server has 3 threads in the executor + _server = new Server(new QueuedThreadPool(3)); + + // connector pool has 100 threads + ThreadPool connectorPool = new QueuedThreadPool(100); + // connector requires 7 threads(2 + 4 + 1) + ServerConnector connector = new ServerConnector(_server, connectorPool, null, null, 2, 4, new HttpConnectionFactory()); + connector.setPort(0); + _server.addConnector(connector); + + // should not throw exception because connector uses own executor, so its threads should not be counted + _server.start(); + } + + @Test // Github issue #586 + public void testCaseForMultipleConnectors() throws Exception { + // server has 3 threads in the executor + _server = new Server(new QueuedThreadPool(3)); + + // first connector consumes all 3 threads from server pool + _server.addConnector(new ServerConnector(_server, null, null, null, 1, 1, new HttpConnectionFactory())); + + // second connect also require 3 threads but uses own executor, so its threads should not be counted + final QueuedThreadPool connectorPool = new QueuedThreadPool(3, 3); + _server.addConnector(new ServerConnector(_server, connectorPool, null, null, 1, 1, new HttpConnectionFactory())); + + // should not throw exception because limit was not overflown + _server.start(); + } + +}