Skip to content

Forward port #12961: assign FastTerminal before starting the build thread - #12962

Merged
gnodet merged 1 commit into
apache:masterfrom
gnodet:forward-port/12912-race
Aug 31, 2026
Merged

Forward port #12961: assign FastTerminal before starting the build thread#12962
gnodet merged 1 commit into
apache:masterfrom
gnodet:forward-port/12912-race

Conversation

@gnodet

@gnodet gnodet commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward port of #12961 to master.

Fixes a race condition introduced in #12921: the FastTerminal constructor started its build thread before returning, so MessageUtils.terminal was still null when the build thread ran the builder callback — a race between the constructor returning and the thread scheduling. This caused a NullPointerException in CI (example):

NullPointerException: Cannot invoke "org.jline.terminal.Terminal.getSize()" because "t" is null

The fix splits construction from start: MessageUtils now assigns the field before calling FastTerminal.start(), and Thread.start() provides the happens-before edge that makes the assignment visible to the build thread without additional synchronization.

  • FastTerminal: extract start() method from constructor, document the publish-before-start contract
  • MessageUtils.systemInstall(): assign terminal field, then call start()
  • New test terminalAssignmentIsVisibleFromBuilderCallback that asserts MessageUtils.getTerminal() is non-null from the builder callback

Test plan

  • FastTerminalReentrancyTest — all 5 tests pass (4 existing + 1 new)
  • Full maven-jline module test suite passes
  • CI validates on Java 17/21/25

🤖 Generated with Claude Code

The FastTerminal constructor started the build thread before
returning, so MessageUtils.terminal was still null when the build
thread ran the builder callback — a race between the constructor
returning and the thread scheduling.

Split construction from start: MessageUtils now assigns the field
before calling FastTerminal.start(), and Thread.start() provides
the happens-before edge that makes the assignment visible to the
build thread.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet added this to the 4.1.0 milestone Aug 30, 2026
@gnodet gnodet added bug Something isn't working forward-port-to-master labels Aug 30, 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.

Clean, correct fix for a real race condition. The JMM analysis is sound — Thread.start() provides the happens-before guarantee (JLS 17.4.5) that makes the MessageUtils.terminal assignment visible to the build thread without requiring volatile.

Highlights:

  • The two-step init (construct then start()) is well-contained — FastTerminal is only constructed in MessageUtils.systemInstall().
  • The test effectively exercises the fix by verifying MessageUtils.getTerminal() returns non-null from within the builder callback on the build thread.
  • This is a clean forward port — the diff is identical to #12961 on maven-4.0.x.

Minor suggestion (non-blocking):

  • The new start() method is public, but its only caller is MessageUtils.systemInstall() in the same package. Making it package-private would prevent accidental misuse from other packages.

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 833a64b into apache:master Aug 31, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working forward-port-to-master

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant