Skip to content

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

Merged
gnodet merged 1 commit into
maven-4.0.xfrom
fix/12912
Aug 30, 2026
Merged

Fix #12912: use a dumb fallback terminal for all build-thread reentrant calls#12921
gnodet merged 1 commit into
maven-4.0.xfrom
fix/12912

Conversation

@gnodet

@gnodet gnodet commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes Maven 4.0.0-rc-6 hanging on startup with JDK 25 in Linux container environments
  • The previous fix for Deadlock when used in maven-executor #12761 guarded only writer() and getType() against reentrant calls from the build thread, but JLine 4.4.0's FFM provider initialization (CLibrary.<clinit>) reaches back through other terminal methods (getSize, getName, encoding, etc.), causing the same deadlock
  • Constructs a DumbTerminal directly (bypassing TerminalBuilder's grapheme-cluster probing) before the build thread starts, and returns it from getTerminal() whenever the build thread would otherwise wait on its own future — covering all delegate methods at once

Fixes #12912

Test plan

  • Existing FastTerminalReentrancyTest tests for writer() and getType() still pass
  • New test arbitraryTerminalMethodsFromTheBuilderDoNotDeadlock verifies getSize() from the builder callback does not deadlock
  • New test arbitraryTerminalMethodsFromTheConsumerDoNotDeadlock verifies getName() from the consumer callback does not deadlock
  • CI passes on all platforms

🤖 Generated with Claude Code

…nt calls

The previous fix for #12761 guarded only writer() and getType() against
reentrant calls from the build thread, because those were the two methods
a log statement exercises. JLine 4.4.0's FFM provider initialization
(CLibrary.<clinit>) can reach back through other terminal methods
(getSize, getName, encoding, etc.) on the build thread, causing the same
deadlock on JDK 25 in container environments.

Construct a DumbTerminal before the build thread starts and return it from
getTerminal() when the build thread would otherwise wait on its own future.
This covers every delegate method at once, regardless of which call path
JLine's provider probing takes. The DumbTerminal is constructed directly
rather than via TerminalBuilder, which probes for grapheme-cluster support
and can block on a null input stream in JLine 4.4.0.

The existing writer() and getType() guards remain for explicitness: they
take priority (checked before getTerminal()) and guarantee the fallback
writer uses the pre-captured System.err.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet added this to the 4.0.0-rc-7 milestone Aug 29, 2026
@gnodet gnodet added the bug Something isn't working label Aug 29, 2026

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-targeted fix for a JDK 25 startup deadlock caused by JLine 4.4.0's FFM provider initialization reaching back through arbitrary terminal methods on the build thread. The approach of using a pre-built DumbTerminal as a catch-all fallback in getTerminal() is sound and minimal.

Key observations:

  • The previous fix (#12761) only guarded writer() and getType(), but JLine 4.4.0's FFM provider init (CLibrary.<clinit>) reaches back through other terminal methods (getSize, getName, encoding, etc.), causing the same deadlock on JDK 25 in containers.
  • The existing writer() and getType() special-case guards are retained for explicitness — they take priority over the new getTerminal() fallback and guarantee the fallback writer uses the pre-captured System.err. This layered approach is clean.
  • The IOException from the DumbTerminal constructor is caught correctly; extremely unlikely in practice with InputStream.nullInputStream().
  • Tests follow the existing pattern with assertTimeoutPreemptively for deadlock detection.

🔀 Forward-port Status

⚠️ The master branch has a simpler FastTerminal without the #12761 buildThread tracking infrastructure. The same deadlock likely affects master too. A combined fix (including the #12761 build-thread tracking + this #12912 DumbTerminal fallback) should be forward-ported to master. Direct cherry-pick will not apply.

The maven-3.9.x and maven-3.10.x branches do not contain the maven-jline module and are not affected.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

@gnodet
gnodet merged commit fa11f3d into maven-4.0.x Aug 30, 2026
43 of 44 checks passed
@gnodet
gnodet deleted the fix/12912 branch August 30, 2026 11:45
gnodet added a commit that referenced this pull request Aug 30, 2026
…nt calls (#12921) (#12934)

The previous fix for #12761 guarded only writer() and getType() against
reentrant calls from the build thread, because those were the two methods
a log statement exercises. JLine 4.4.0's FFM provider initialization
(CLibrary.<clinit>) can reach back through other terminal methods
(getSize, getName, encoding, etc.) on the build thread, causing the same
deadlock on JDK 25 in container environments.

Construct a DumbTerminal before the build thread starts and return it from
getTerminal() when the build thread would otherwise wait on its own future.
This covers every delegate method at once, regardless of which call path
JLine's provider probing takes. The DumbTerminal is constructed directly
rather than via TerminalBuilder, which probes for grapheme-cluster support
and can block on a null input stream in JLine 4.4.0.

The existing writer() and getType() guards remain for explicitness: they
take priority (checked before getTerminal()) and guarantee the fallback
writer uses the pre-captured System.err.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@slawekjaranowski

Copy link
Copy Markdown
Member

@gnodet

The maven-3.9.x and maven-3.10.x branches do not contain the maven-jline module and are not affected.

maven-3.10.x branch use JLine 3.x after Migrate from JAnsi to JLine in:

There is a https://github.com/apache/maven/tree/maven-3.10.x/maven-jline module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants