Thanks for the report.

For TestLargeClientHello the cause is in TomcatBaseTest.LogCapture.
logRecords is a Collections.synchronizedList(), so add() is safe, but
containsText() and hasException() iterate it without locking, which the
synchronizedList Javadoc says the caller has to do. The test polls
containsText() and then calls hasException() while  the server threads are
still logging at FINE, so they sometimes overlap.
A test that adds records from one thread through LogCapture.handler while
the test thread calls hasException() and containsText() fails with
ConcurrentModificationException after a few milliseconds with the current
code, and passes with the fix below.
The fix is to wrap the two loops in synchronized (logRecords) { ... }. With
it TestLargeClientHello (5 runs), TestPerWebappJuliIntegration,
TestTldScanner and TestTomcatStandalone pass (Java 17, 11.0.28 tarball). I
could not reproduce the failure inside TestLargeClientHello itself as it is
sporadic.

Arturo


On Mon, Nov 9, 2026 at 2:41 PM Rainer Jung <[email protected]> wrote:

> Thanks Arturo!
>
> Another sporadic one in org.apache.tomcat.util.net.TestLargeClientHello:
>
> Testcase: testClientHelloLargerThanParseLimit took 4.741 sec
>          Caused an ERROR
> null
> java.util.ConcurrentModificationException
>          at
>
> java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
>          at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
>          at
>
> org.apache.catalina.startup.TomcatBaseTest$LogCapture.hasException(TomcatBaseTest.java:1005)
>          at
> org.apache.tomcat.util.net
> .TestLargeClientHello.testClientHelloLargerThanParseLimit(TestLargeClientHello.java:144)
>          at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
> Method)
>          at
>
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
>          at
>
> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>
>
> Am 09.11.26 um 14:08 schrieb Arturo Bernal via dev:
> > I looked at the three failures with the 11.0.28 source tarball and Java
> 17.
> >
> > 1. TestAbstractEndpoint (2 errors) The two virtual thread tests need Java
> > 21 and 11.0.x supports Java 17. Adding
> > Assume.assumeTrue(JreCompat.isJre21Available()), as
> > TestStandardVirtualThreadExecutor does, skips them on Java 17 and they
> > still run on Java 21.
> >
> > 2. TestJspCServletContext.testGetResourceWithUriIllegalCharacters It
> passes
> > with a UTF-8 locale and fails with LANG=C (InvalidPathException on
> > unicode-ü.jsp). An Assume that sun.jnu.encoding can encode the character
> > skips it in that case.
> >
> > 3. TestUpgrade.testUpgradeInfoUnregisteredWhenConnectionClosed I could
> not
> > reproduce it as it is (8 runs with NIO and 8 with NIO2). I think I found
> > the cause. In ConnectionHandler.process() the processor is taken from the
> > socket wrapper on entry and only set back at the end. The UpgradeInfo is
> > registered earlier, in the processor constructor. If the endpoint closes
> > the  socket in between, which is what stop() does, release(socketWrapper)
> > finds no processor, recycle() is never called and the UpgradeInfo stays
> > registered.
> >
> > To check this I added Thread.sleep(1500) to NoClose.init() in the test.
> > With  that, NIO2 fails every time with the same message and the same line
> > (TestUpgrade.java:439) and NIO passes.
> >
> > This change fixes it, on top of setCurrentProcessor() in process():
> >
> >      if (wrapper.isClosed()) {
> >          // The socket was closed while this thread owned the processor
> so
> >          // the close could not release it.
> >          release(wrapper);
> >      }
> >
> > takeCurrentProcessor() is atomic so only one thread can release the
> > processor. With it the sleep version of the test passes with NIO and
> NIO2,
> > and  all the coyote tests pass with NIO and NIO2 (75 classes, 9322
> tests).
> >
> > Not tested on main, only on the 11.0.28 tarball.
> >
> > Arturo
> >
> >
> > On Mon, Nov 9, 2026 at 1:24 PM Mark Thomas via dev <
> [email protected]>
> > wrote:
> >
> >> Thanks Rainer. I'll take a look before I tag 11.0.29
> >>
> >> Mark
> >>
> >>
> >> On 09/11/2026 12:08, Rainer Jung wrote:
> >>> plus sporadic failures in org.apache.coyote.http11.upgrade.TestUpgrade
> >>> (until now only observed in NIO2, but that might be pure coincidence):
> >>>
> >>> Testcase: testUpgradeInfoUnregisteredWhenConnectionClosed took 10.176
> sec
> >>>           FAILED
> >>> The UpgradeInfo remained registered after the connection was closed.
> >>> expected:<0> but was:<1>
> >>> junit.framework.AssertionFailedError: The UpgradeInfo remained
> >>> registered after the connection was closed. expected:<0> but was:<1>
> >>>           at
> >>>
> >>
> org.apache.coyote.http11.upgrade.TestUpgrade.testUpgradeInfoUnregisteredWhenConnectionClosed(TestUpgrade.java:439)
> >>>           at java.base/
> >>> jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >>>           at java.base/
> >>>
> >>
> jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
> >>>           at java.base/
> >>>
> >>
> jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
>

Reply via email to