shutdown hook
This commit is contained in:
@@ -236,6 +236,18 @@ public final class Compositor {
|
||||
tabs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals and reaps every pane's shell process across all tabs, without tearing down render
|
||||
* state. Intended for a JVM shutdown hook (SIGTERM/SIGINT/SIGHUP), so child shells get the
|
||||
* configured close signal instead of being orphaned when jprototerm itself is killed. Safe to
|
||||
* call off the FX thread and idempotent; see {@link TerminalPane#terminateSession()}.
|
||||
*/
|
||||
public void terminateSessions() {
|
||||
for (Tab tab : List.copyOf(tabs)) {
|
||||
tab.terminateSessions();
|
||||
}
|
||||
}
|
||||
|
||||
private Tab currentTab() {
|
||||
return tabs.get(currentTabIndex);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,12 @@ public final class Main extends Application {
|
||||
compositor.close();
|
||||
Platform.exit();
|
||||
});
|
||||
// If jprototerm itself is killed (SIGTERM/SIGINT/SIGHUP, e.g. a logout or `kill`), the JVM
|
||||
// runs shutdown hooks before exiting. Send each pane's configured close signal here so the
|
||||
// child shells are terminated rather than orphaned. Only the ptys are touched (not ghostty's
|
||||
// native state), so this is safe to run concurrently with the still-live render loop. A
|
||||
// SIGKILL of jprototerm bypasses hooks entirely; nothing can help there.
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(compositor::terminateSessions, "shell-cleanup"));
|
||||
|
||||
StackPane root = new StackPane(compositor.canvas(), compositor.imageOverlay());
|
||||
compositor.canvas().widthProperty().bind(root.widthProperty());
|
||||
|
||||
@@ -423,4 +423,14 @@ final class Tab implements AutoCloseable {
|
||||
tiled.clear();
|
||||
floating.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals and reaps every pane's shell process without tearing down render state. Safe to call
|
||||
* off the FX thread (see {@link TerminalPane#terminateSession()}); iterates snapshots so a
|
||||
* concurrent close on the FX thread can't trigger a {@link java.util.ConcurrentModificationException}.
|
||||
*/
|
||||
public void terminateSessions() {
|
||||
List.copyOf(tiled).forEach(TerminalPane::terminateSession);
|
||||
List.copyOf(floating).forEach(TerminalPane::terminateSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
||||
// tracking meaningful: update() accumulates dirty since the last resetDirty().
|
||||
private final RenderState renderState = new RenderState();
|
||||
private RenderStateSnapshot cachedSnapshot;
|
||||
private ShellSession session;
|
||||
private volatile ShellSession session;
|
||||
// Run once (on the FX thread) when this pane's process exits on its own, so the owning tab can
|
||||
// remove it. Set by the Tab that creates the pane; null until then.
|
||||
private Runnable onExit;
|
||||
@@ -380,4 +380,17 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
||||
renderState.close();
|
||||
terminal.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals and reaps just the shell process, leaving the render/native state untouched. Unlike
|
||||
* {@link #close()} this is safe to call off the FX thread — notably from a JVM shutdown hook,
|
||||
* which runs concurrently with the live render loop — because it only touches the pty (a child
|
||||
* process and fd), not ghostty's terminal handles. Idempotent; the OS reclaims the rest on exit.
|
||||
*/
|
||||
public void terminateSession() {
|
||||
ShellSession current = session;
|
||||
if (current != null) {
|
||||
current.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user