This is an automated email from the ASF dual-hosted git repository. markt-asf pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 26485d700ce45ce031c0f8437731ad5beb44a406 Author: Mark Thomas <[email protected]> AuthorDate: Tue Oct 20 18:50:44 2026 +0100 Fix intermittent test failures Co-authored by: Claude / Sonnet 5.5 --- .../apache/tomcat/util/threads/TestTaskQueue.java | 37 ++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/test/org/apache/tomcat/util/threads/TestTaskQueue.java b/test/org/apache/tomcat/util/threads/TestTaskQueue.java index 5c39ddf0dc..96b827431c 100644 --- a/test/org/apache/tomcat/util/threads/TestTaskQueue.java +++ b/test/org/apache/tomcat/util/threads/TestTaskQueue.java @@ -30,7 +30,6 @@ public class TestTaskQueue { private static final long serialVersionUID = 1L; private final AtomicInteger pollCount = new AtomicInteger(); - @Override public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { pollCount.incrementAndGet(); @@ -61,15 +60,38 @@ public class TestTaskQueue { CountDownLatch executed = new CountDownLatch(4); executor.execute(executed::countDown); executor.execute(executed::countDown); - // Wait for both workers to be created and to enter take() - Thread.sleep(200); + // Wait for both tasks to complete and for the workers to finish with them. A worker that is still + // running a task when the threads are marked as due to be stopped retires itself without waiting. + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5); + while ((executed.getCount() != 2 || executor.getActiveCount() != 0) && System.nanoTime() < deadline) { + Thread.sleep(10); + } + Assert.assertEquals(2, executed.getCount()); + Assert.assertEquals(0, executor.getActiveCount()); + // Threads are only due to be stopped if they were created strictly before the context stopped (compared + // in milliseconds) so make sure the clock has moved on from the creation of the workers + long created = System.currentTimeMillis(); + while (System.currentTimeMillis() <= created) { + Thread.sleep(1); + } - // Mark the (older) worker threads as due to be stopped + // Mark the (older) worker threads as due to be stopped. This interrupts the idle workers which then + // re-enter take() which, since the threads are due to be stopped, switches to poll() with a bounded + // timeout. executor.contextStopping(); + + // Both workers must have entered the renewal wait. Do this before submitting more tasks. If a task is + // queued while the interrupt is still being delivered, the worker may return the task from take() rather + // than being interrupted and the renewal wait would not be entered. + deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5); + while (queue.pollCount.get() < 2 && System.nanoTime() < deadline) { + Thread.sleep(10); + } + Assert.assertTrue("Renewal wait was not exercised", queue.pollCount.get() >= 2); + + // The workers complete these tasks and then re-enter take() executor.execute(executed::countDown); executor.execute(executed::countDown); - // The workers complete these tasks and then re-enter take() which, since the threads are due to be - // stopped, switches to poll() with a bounded timeout Assert.assertTrue(executed.await(5, TimeUnit.SECONDS)); int pollsAfterTask = queue.pollCount.get(); @@ -79,9 +101,6 @@ public class TestTaskQueue { // Without the bounded timeout this is orders of magnitude larger (millions in this window) Assert.assertTrue("Unexpected poll count while idle: " + spinIterations, spinIterations < 100); - // Both workers must have entered the renewal wait at least once - Assert.assertTrue("Renewal wait was not exercised", queue.pollCount.get() >= 2); - // The workers must still be able to process tasks CountDownLatch processed = new CountDownLatch(1); executor.execute(processed::countDown); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
