Skip to content

Guard console appender against writing to a closed terminal during shutdown#579

Open
mvanhorn wants to merge 1 commit into
CleanroomMC:mainfrom
mvanhorn:fix/566-console-terminal-closed
Open

Guard console appender against writing to a closed terminal during shutdown#579
mvanhorn wants to merge 1 commit into
CleanroomMC:mainfrom
mvanhorn:fix/566-console-terminal-closed

Conversation

@mvanhorn

Copy link
Copy Markdown

Summary

Stops the ERROR An exception occurred processing Appender Console / java.lang.IllegalStateException: Terminal has been closed stack trace that log4j prints on every clean dedicated-server stop (#566). The shutdown already completed correctly, but the spurious trace was logged each time stop was typed.

Why this matters

As the maintainer confirmed on the issue, MinecraftServer.stopServer() keeps logging ("Stopping server", "Saving players", ...) after the JLine terminal has already been closed during shutdown, so those log events are dispatched to a dead appender.

TerminalConsoleAppender.print() is synchronized on the instance, while close() and setReader() are synchronized on the class. Because they use different monitors, the static terminal/reader fields can be cleared, or the terminal closed, concurrently with a print() that has already passed its null check. The call then lands on reader.printAbove(...) / terminal.writer().print(...) against a closed terminal and throws IllegalStateException: Terminal has been closed.

The fix:

  • print() captures the shared terminal/reader into locals (so a concurrent clear cannot turn the guard into an NPE) and wraps the JLine write in try/catch (IllegalStateException), falling back to the saved stdout PrintStream exactly as the no-terminal branch already does. Late shutdown log lines still reach stdout instead of failing the appender.
  • close() now also nulls the stale reader after closing the terminal, so a later event cannot reuse a LineReader bound to the closed terminal.

Normal runtime logging is unchanged: while the terminal is open and a LineReader is attached, reader.printAbove(...) is still used.

Testing

  • Reviewed the shutdown path: TerminalHandler only clears the reader in its finally after the read loop exits, so during stopServer() the reader/terminal can still be set while the terminal is being closed - the exact race the catch now absorbs.
  • Confirmed the change is self-contained to TerminalConsoleAppender and that the open-terminal path (reader.printAbove / terminal.writer()) is preserved; only the closed-terminal case is rerouted to stdout.

Fixes #566

…utdown

When a dedicated server is stopped, MinecraftServer.stopServer() keeps
logging after the JLine terminal has been closed, so log events are
dispatched to a dead appender and log4j prints
'IllegalStateException: Terminal has been closed' on every clean stop.

print() synchronizes on the instance while close()/setReader() synchronize
on the class, so the terminal/reader fields can be cleared (or the terminal
closed) concurrently. Capture them into locals and catch IllegalStateException,
falling back to the saved stdout PrintStream. Also drop the stale reader in
close() so a later event cannot reuse a reader bound to the closed terminal.

Fixes CleanroomMC#566
stdout.print(text);
}

stdout.print(text);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should put this in the catch block, otherwise it’d print with both the Terminal’s writer and to stdout unconditionally?

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.

strange error in server terminal after stopping it

2 participants