Reported by RV-Predict (a dynamic race detector) when running the test suite: Data race on field org.apache.catalina.util.LifecycleSupport.listeners: {{{ Concurrent write in thread T19 (locks held: {Monitor@49fb3aa9, Monitor@5b24cbd}) ----> at org.apache.catalina.util.LifecycleSupport.addLifecycleListener(LifecycleSupport.java:87) - locked Monitor@5b24cbd at org.apache.catalina.util.LifecycleSupport.addLifecycleListener(LifecycleSupport.java:81) at org.apache.catalina.util.LifecycleBase.addLifecycleListener(LifecycleBase.java:61) at org.apache.catalina.core.StandardEngine$AccessLogListener.install(StandardEngine.java:449) at org.apache.catalina.core.StandardEngine.logAccess(StandardEngine.java:337) at org.apache.catalina.connector.CoyoteAdapter.log(CoyoteAdapter.java:675) at org.apache.coyote.http11.Http11Nio2Processor.handleIncompleteRequestLineRead(Http11Nio2Processor.java:242) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1006) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:663) at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.doRun(Nio2Endpoint.java:1074) at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.run(Nio2Endpoint.java:1033) - locked Monitor@49fb3aa9 at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.run(Nio2Endpoint.java:1032) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) T19 is created by T18 at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:1010) Concurrent read in thread T13 (locks held: {Monitor@632e842a}) ----> at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:115) at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90) at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:402) at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:347) at org.apache.catalina.core.ContainerBase.stopInternal(ContainerBase.java:954) at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232) - locked Monitor@632e842a at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:n/a) at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1424) T13 is created by T1 at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:1010) }}}
This bug is detected by Coverity Scan as well. I dig into the history and it appears that this class was originally written with proper synchronization. Anyway, listeners + listenersLock looks like a home-made CopyOnWriteArrayList. Any reason not to use a concurrent data structure such as ConcurrentLinkedQueue or concurrent HashSet?
Fixed in trunk and 8.0.x for 8.0.27 onwards.