Skip to content

fix: let an interrupt stop the parse, release striff-lib 4.3.3 - #72

Merged
Zir0-93 merged 3 commits into
masterfrom
fix/interruptible-parallel-parse
Sep 14, 2026
Merged

Zir0-93 merged 3 commits into
masterfrom
fix/interruptible-parallel-parse

Conversation

@Zir0-93

@Zir0-93 Zir0-93 commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

Closes #73.

Releases striff-lib 4.3.3.

Problem

StriffOperation parsed the base and head revisions with CompletableFuture.supplyAsync on the common pool and waited with join(). join() ignores interrupts, and the threads doing the parsing were never the thread a caller interrupts. Clarpse's cooperative cancellation checks watch the thread they run on, so they never fired: a caller enforcing a time budget with Thread.interrupt() could not stop a parse. The parse ran to completion, and the interrupt was only noticed afterwards at the CodeDiff merge checkpoint.

Change

  • New package-private ParallelParse runs the two parses on a two-thread pool it owns. The caller waits interruptibly; an interrupt cancels both parses (interrupting their threads, where Clarpse's checks see it) and the call throws CancellationException with the interrupt flag re-asserted, matching the existing checkpoints in CodeDiff and ExtractedRelationships.
  • No parse outlives the call: whatever ends the wait, both threads are stopped and awaited before it returns. A parse that ignores its interrupt still runs to completion, as before; it is never reported as stopped when it is not.
  • Results are taken in completion order, so one failed parse ends the wait and stops the other instead of leaving it running in the background.
  • Behaviour note: a CompileException from a parse now reaches the caller as itself. It was previously wrapped in a RuntimeException by the lambda, which defeated the unwrapping joinCompileResult intended.
  • README gains a short Cancellation section; StriffOperation's full-pipeline constructor documents it.
  • Version 4.3.2 → 4.3.3.

Verification

  • mvn clean package -B on JDK 17 and JDK 21: 213 tests, 0 failures; Checkstyle and PMD clean. PMD's CloseResource is suppressed on ParallelParse.run: ExecutorService is AutoCloseable only from Java 19, so try-with-resources is unavailable at the Java 17 baseline, and the pool is shut down and awaited in a finally.
  • ParallelParseTest (4 tests): the interrupt reaches both parse threads and the call returns only after both parses have ended; a failed parse stops the other without waiting for it; ordinary results are returned by revision; an interrupted StriffOperation throws CancellationException. Thread-count checks allow a pool thread a bounded moment to finish exiting, since a pool reports termination from inside its last worker; the parses having ended is pinned by latches. 10 repeated runs on JDK 21 passed.
  • Red check: with ParallelParse temporarily reverted to the old supplyAsync + join() wait, two of the new tests fail for the expected reasons: the interrupt test ("the call must return once its thread is interrupted") and the failed-parse test (times out waiting behind the blocked parse).

🤖 Generated with Claude Code

https://claude.ai/code/session_01ACYxqmHapz2iiCJn16tVhj

Zir0-93 and others added 3 commits September 15, 2026 00:27
StriffOperation parsed the base and head revisions with
CompletableFuture.supplyAsync on the common pool and waited with join().
join() ignores interrupts, and the threads doing the parsing were never
the one a caller interrupts, so Clarpse's cooperative cancellation checks,
which watch the thread they run on, never fired. A caller enforcing a time
budget with Thread.interrupt() could not stop a parse: it ran to completion
and the interrupt was only noticed afterwards, at the merge checkpoint.

The two parses now run on a small pool owned by ParallelParse. The caller
waits interruptibly; an interrupt cancels both parses, which interrupts
their threads, and the call returns only once both have stopped, throwing
CancellationException with the interrupt flag re-asserted. Results are
taken in completion order, so one failed parse ends the wait and stops the
other instead of leaving it running in the background. A CompileException
from a parse now reaches the caller as itself rather than wrapped in a
RuntimeException.

Version 4.3.2 -> 4.3.3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACYxqmHapz2iiCJn16tVhj
ExecutorService became AutoCloseable in Java 19, so PMD's CloseResource
flags the pool when the build runs on a newer JDK. try-with-resources is
not available at the Java 17 baseline; the pool is shut down and awaited
by stop() in the finally, so the rule is suppressed on that method with
the reason noted where the pool is created.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACYxqmHapz2iiCJn16tVhj
A thread pool reports termination from inside its last worker, so that
thread can still be alive for an instant after the call returns, while it
unwinds and after its parse has ended. Counting live threads at that
instant raced it. The thread checks now give each thread a bounded time to
finish exiting; that no parse is still running when the call returns stays
pinned by the latch checks. The javadoc now states the guarantee in terms
of the parses having ended rather than the threads having stopped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACYxqmHapz2iiCJn16tVhj
@Zir0-93
Zir0-93 merged commit 0909182 into master Sep 14, 2026
6 checks passed
@Zir0-93
Zir0-93 deleted the fix/interruptible-parallel-parse branch September 14, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interrupting a StriffOperation does not stop the parse

1 participant