From 2534a3e2f9e66152c5ccf6ac264c028e27d203f5 Mon Sep 17 00:00:00 2001 From: andreatp Date: Wed, 21 Oct 2026 10:17:07 +0100 Subject: [PATCH] Stop MemoryTest hanging CI on macOS waitAndNotifyWithAtomicsOnSameWord ran a task that never ends on the ForkJoin common pool, submitted its waiter to the same pool, then spun with no deadline until the waiter was parked. On JDK 17 and 21 the common pool, which has two workers on the 3-core macOS runners, can leave that waiter queued while its idle worker stays parked, so the spin never ends and the job hangs until GitHub's 6 hour timeout. Run both tasks on two threads of their own instead. The CI job also gets a 60 minute timeout, about twice its usual duration, so a hang can no longer hold a runner for 6 hours. --- .github/workflows/ci.yaml | 2 ++ .../src/test/java/run/endive/runtime/MemoryTest.java | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 54a2c3d87..99d1fdfd1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,6 +9,8 @@ jobs: ci: name: CI runs-on: ${{ matrix.os }} + # a hung test fails in an hour instead of at GitHub's 6 hour default + timeout-minutes: 60 continue-on-error: true strategy: fail-fast: false diff --git a/runtime/src/test/java/run/endive/runtime/MemoryTest.java b/runtime/src/test/java/run/endive/runtime/MemoryTest.java index a9ddd6e83..6fb6cecdc 100644 --- a/runtime/src/test/java/run/endive/runtime/MemoryTest.java +++ b/runtime/src/test/java/run/endive/runtime/MemoryTest.java @@ -6,6 +6,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -285,18 +286,22 @@ public void waitAndNotifyWithAtomicsOnSameWord(String name, Supplier mem memory.atomicWriteInt(addr, 0); var stop = new AtomicBoolean(); + // not the common pool: on a 3-core runner it can leave the waiter queued behind adds + var threads = Executors.newFixedThreadPool(2); var adds = CompletableFuture.runAsync( () -> { while (!stop.get()) { memory.atomicAddInt(0, 1); } - }); + }, + threads); try { for (int round = 0; round < 100; round++) { var waiter = CompletableFuture.supplyAsync( - () -> memory.atomicWait(addr, 0, TimeUnit.SECONDS.toNanos(30))); + () -> memory.atomicWait(addr, 0, TimeUnit.SECONDS.toNanos(30)), + threads); while (memory.atomicNotify(addr, 1) == 0 && !waiter.isDone()) { Thread.onSpinWait(); } @@ -304,6 +309,7 @@ public void waitAndNotifyWithAtomicsOnSameWord(String name, Supplier mem } } finally { stop.set(true); + threads.shutdown(); } adds.get(30, TimeUnit.SECONDS); }