diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index 3d14aa2d36c..f3c9134b4a0 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -131,7 +131,8 @@ public class OpenIdConfiguration implements Serializable public void addScopes(String... scopes) { - Collections.addAll(this.scopes, scopes); + if (scopes != null) + Collections.addAll(this.scopes, scopes); } public List getScopes() diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java index d34792634ef..eaf4819a96f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java @@ -155,6 +155,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec public void doStart() throws Exception { _lease = ThreadPoolBudget.leaseFrom(getExecutor(), this, _capacity); + _size.set(0); super.doStart(); } @@ -163,15 +164,29 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec { if (_lease != null) _lease.close(); + + super.doStop(); + while (true) { + int size = _size.get(); + // If no reserved threads left try setting size to -1 to + // atomically prevent other threads adding themselves to stack. + if (size == 0 && _size.compareAndSet(size, -1)) + break; + ReservedThread thread = _stack.pollFirst(); if (thread == null) - break; + { + // Reserved thread must have incremented size but not yet added itself to queue. + // We will spin until it is added. + Thread.onSpinWait(); + continue; + } + _size.decrementAndGet(); thread.stop(); } - super.doStop(); } @Override @@ -275,11 +290,12 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec if (LOG.isDebugEnabled()) LOG.debug("{} waiting", this); - Runnable task = null; - while (task == null) + while (true) { - boolean idle = false; + if (!isRunning()) + return STOP; + boolean idle = false; try (AutoLock lock = _lock.lock()) { if (_task == null) @@ -296,8 +312,16 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec LOG.ignore(e); } } - task = _task; - _task = null; + else + { + Runnable task = _task; + _task = null; + + if (LOG.isDebugEnabled()) + LOG.debug("{} task={}", this, task); + + return task; + } } if (idle) @@ -312,11 +336,6 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec tryExecute(STOP); } } - - if (LOG.isDebugEnabled()) - LOG.debug("{} task={}", this, task); - - return task; } @Override @@ -329,6 +348,8 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec while (true) { int size = _size.get(); + if (size < 0) + return; if (size >= _capacity) { if (LOG.isDebugEnabled()) diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java index 434e12c90b1..30a7b37431d 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java +++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java @@ -19,20 +19,16 @@ package org.eclipse.jetty.websocket.tests; import java.net.URI; -import java.nio.channels.ClosedChannelException; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; -import org.eclipse.jetty.websocket.common.WebSocketSession; -import org.eclipse.jetty.websocket.core.internal.compress.CompressExtension; import org.eclipse.jetty.websocket.server.JettyWebSocketServlet; import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; @@ -108,7 +104,8 @@ public class WriteAfterStopTest assertThat(clientSocket.statusCode, is(StatusCode.NORMAL)); assertThat(serverSocket.statusCode, is(StatusCode.NORMAL)); - ((WebSocketSession)session).stop(); - assertThrows(IllegalStateException.class, () -> session.getRemote().sendString("hello world")); + IllegalStateException failure = assertThrows(IllegalStateException.class, + () -> session.getRemote().sendString("this should fail before ExtensionStack")); + assertThat(failure.getMessage(), is("CLOSED")); } } \ No newline at end of file