Sitelet https://github.com/apache/maven/pull/12925
Skip to content

Fix #12912: use a dumb fallback terminal for all build-thread reentrant calls - #12925

Closed
gnodet wants to merge 1 commit into
masterfrom
fix/12912-guard-close
Closed

gnodet wants to merge 1 commit into
masterfrom
fix/12912-guard-close

Conversation

@gnodet

@gnodet gnodet commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes Maven 4.0.0-rc-6 hanging on startup with JDK 25 in Linux container environments, and fixes a flaky FastTerminalReentrancyTest.usingTheTerminalFromTheConsumerDoesNotDeadlock deadlock observed across JDK 17/21/25 on ubuntu CI runners (example, example, example).

Root cause: FastTerminal only guarded writer() and getType() against build-thread reentrancy. JLine 4.4.0's FFM provider initialization (CLibrary.<clinit>) and AnsiConsole.systemInstall() can reach back through other terminal methods (getSize, getName, encoding, etc.) on the build thread, causing a deadlock where the build thread waits on the CompletableFuture it is itself computing.

Fix: Move the isBuildThreadWaitingOnItself() guard into getTerminal() — the single choke point all delegate methods pass through — backed by a pre-built DumbTerminal. This covers all methods at once, including any new ones JLine may add.

Improvements over #12921:

  • The DumbTerminal's output stream is wrapped with FilterOutputStream so that close() flushes instead of closing the captured System.err — DumbTerminal.doClose() cascades to its writer's underlying stream, and closing System.err would silently break stderr for the process
  • Removed the now-redundant per-method guards on writer() and getType(), along with the unused fallbackWriter field and fallbackWriter() method — the single guard in getTerminal() makes them dead code

Test plan

  • Existing tests for writer() and getType() still pass (4 tests, 10/10 stress runs)
  • New test arbitraryTerminalMethodsFromTheBuilderDoNotDeadlock verifies getSize() from the builder callback
  • New test arbitraryTerminalMethodsFromTheConsumerDoNotDeadlock verifies getName() from the consumer callback
  • CI passes on all platforms

Supersedes #12921.

🤖 Generated with Claude Code

…nt calls

Guard getTerminal() instead of individual methods so every delegate is
covered — JLine 4.4.0's FFM provider initialization reaches back through
arbitrary terminal methods on the build thread. The DumbTerminal fallback
output is wrapped with FilterOutputStream to prevent close() from closing
the captured System.err.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet

gnodet commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of the forward-port PR from the squash-merged #12921.

@gnodet gnodet closed this Aug 30, 2026
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.

1 participant