https://issues.apache.org/bugzilla/show_bug.cgi?id=56905

            Bug ID: 56905
           Summary: "Unable to destroy WebSocket thread group" warning
                    when reloading examples webapp
           Product: Tomcat 8
           Version: 8.0.11
          Hardware: PC
            Status: NEW
          Severity: minor
          Priority: P2
         Component: WebSocket
          Assignee: dev@tomcat.apache.org
          Reporter: knst.koli...@gmail.com

Testing 8.0.12 release candidate.
Windows, JDK 7u67 (32-bit), NIO connector.

Steps to reproduce:
==========
1. Start Tomcat

2. Visit "Echo" websockets example and do the following
http://localhost:8080/examples/websocket/echo.xhtml
- Select any connection option
(I like "(*) annotation API (stream)", but the issue is reproducible with any
of the 3 connection options)
- Press "Connect" button
- Press "Echo message" button
- Press "Disconnect" button

3. Touch file webapps\examples\WEB-INF\web.xml and wait for the web application
to be reloaded by Tomcat

Actual: Webapp reloading completes successfully, but a "Unable to destroy
WebSocket thread group" warning is logged into logs/catalina.2014-08-31.log
file:
Expected: No warning.

[[[
31-Aug-2014 18:20:22.357 INFO
[ContainerBackgroundProcessor[StandardEngine[Catalina]]]
org.apache.catalina.core.StandardContext.reload Reloading Context with name
[/examples] has started
31-Aug-2014 18:20:22.362 WARNING
[ContainerBackgroundProcessor[StandardEngine[Catalina]]]
org.apache.tomcat.websocket.server.WsServerContainer.destroy Unable to destroy
WebSocket thread group [WebSocketServer-localhost-/examples] as some threads
were still running when the web application was stopped
31-Aug-2014 18:20:22.877 INFO
[ContainerBackgroundProcessor[StandardEngine[Catalina]]]
org.apache.catalina.core.StandardContext.reload Reloading Context with name
[/examples] is completed
]]]

Evaluation, notes
==========
1) Reproduction rate is less than 100%. Sometimes the warning does not happen.
Steps 2-4 can be repeated without restarting Tomcat until the issue shows
itself.
In the example, any connection option can be used. The issue does not depend on
it.

2) The message is printed by
org.apache.tomcat.websocket.server.WsServerContainer.destroy() method and
originates from r1589043

3) I tried to get more information, by adding the following debug code:
[[[
            StringBuilder buf = new StringBuilder();
            buf.append(threadGroup.getName());
            buf.append(" activeCount: ").append(threadGroup.activeCount());
            buf.append(" isDestroyed: ").append(threadGroup.isDestroyed());
            Thread[] threads = new Thread[100];
            int threadCount = threadGroup.enumerate(threads, false);
            buf.append("\nactual Thread count: ").append(threadCount);
            for (int i=0; i<threadCount; i++) {
                buf.append("\n\n").append(threads[i]);
                StackTraceElement[] stack = threads[i].getStackTrace();
                for (StackTraceElement ste: stack) {
                    buf.append("\n\t").append(ste);
                }
            }
            log.warn(buf.toString());
]]]

The results:
1. activeCount: 0 isDestroyed: false
2. actual Thread count: 0, threadGroup.enumerate() have not returned any
thread.
3. If I add second threadGroup.destroy(); call after the above code, it
succeeds.

4) There are no PermGen memory leaks. (No thread stack traces are printed by
WebappClassLoader leak detection code. No leaks detected by Find leaks command
in Tomcat Manager webapp).

Missing a ThreadGroup.destroy() call will keep this thread group in its parent
thread group's list, so there will be a small java object leak.

5) In this case we were safe, but generally there might be threads that are
still running, as they perform some web application code.
So the warning is justified.

Thoughts on a possible fix
==========
1) Use threadGroup.setDaemon(true) [1].

The daemon flag on a thread group means that its destroy() method will be
called automatically when its thread count reaches zero.

Generally it either shall be done in a synchronized(threadGroup) block, or we
shall call some synchronized methods later to make sure that the change is
propagated. (E.g. calling ThreadGroup.isDestroyed(), ThreadGroup.activeCount()
is good for this).

The goal is to avoid the java object leak.

[1]
http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadGroup.html#setDaemon%28boolean%29

2) Ask for threadGroup.activeCount() and include the count in the warning
message.

-- 
You are receiving this mail because:
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to