--- java/org/apache/tomcat/websocket/server/LocalStrings.properties (revision 1621698) +++ java/org/apache/tomcat/websocket/server/LocalStrings.properties (working copy) @@ -22,7 +22,7 @@ serverContainer.pojoDeploy=POJO class [{0}] deploying to path [{1}] in ServletContext [{2}] serverContainer.servletContextMismatch=Attempted to register a POJO annotated for WebSocket at path [{0}] in the ServletContext with context path [{1}] when the WebSocket ServerContainer is allocated to the ServletContext with context path [{2}] serverContainer.servletContextMissing=No ServletContext was specified -serverContainer.threadGroupNotDestroyed=Unable to destroy WebSocket thread group [{0}] as some threads were still running when the web application was stopped +serverContainer.threadGroupNotDestroyed=Unable to destroy WebSocket thread group [{0}] as {1} threads were still running when the web application was stopped uriTemplate.duplicateParameter=The parameter [{0}] appears more than once in the path which is not permitted uriTemplate.emptySegment=The path [{0}] contains one or more empty segments which are is not permitted --- java/org/apache/tomcat/websocket/server/WsServerContainer.java (revision 1621698) +++ java/org/apache/tomcat/websocket/server/WsServerContainer.java (working copy) @@ -273,13 +273,42 @@ public void destroy() { shutdownExecutor(); super.destroy(); + // If the executor hasn't fully shutdown it won't be possible to + // destroy this thread group as there will still be threads running. + // Mark the thread group as daemon one, so that it destroys itself + // when thread count reaches zero. + // Synchronization on threadGroup is needed, as there is a race between + // destroy() call from termination of the last thread in thread group + // marked as daemon versus the explicit destroy() call. + int threadCount = threadGroup.activeCount(); + boolean success = false; try { - threadGroup.destroy(); - } catch (IllegalThreadStateException itse) { - // If the executor hasn't fully shutdown it won't be possible to - // destroy this thread group as there will still be threads running + while (true) { + int oldThreadCount = threadCount; + synchronized (threadGroup) { + if (threadCount > 0) { + Thread.yield(); + threadCount = threadGroup.activeCount(); + } + if (threadCount > 0 && threadCount != oldThreadCount) { + // Value not stabilized. Retry. + continue; + } + if (threadCount > 0) { + threadGroup.setDaemon(true); + } else { + threadGroup.destroy(); + success = true; + } + break; + } + } + } catch (IllegalThreadStateException exception) { + // Fall-through + } + if (!success) { log.warn(sm.getString("serverContainer.threadGroupNotDestroyed", - threadGroup.getName())); + threadGroup.getName(), Integer.valueOf(threadCount))); } }