instantly close panes instead of waiting for process to end

This commit is contained in:
2026-06-05 11:10:04 +02:00
parent 55a1f2cf6d
commit 217e865448
4 changed files with 61 additions and 6 deletions

View File

@@ -15,6 +15,8 @@ import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** /**
* A Linux PTY backed by libc via the Foreign Function & Memory API. * A Linux PTY backed by libc via the Foreign Function & Memory API.
@@ -33,6 +35,11 @@ import java.util.Map;
public final class LinuxPty implements AutoCloseable { public final class LinuxPty implements AutoCloseable {
static final Linker LINKER = Linker.nativeLinker(); static final Linker LINKER = Linker.nativeLinker();
private static final SymbolLookup LIBC = LINKER.defaultLookup(); private static final SymbolLookup LIBC = LINKER.defaultLookup();
private static final ExecutorService REAPER = Executors.newCachedThreadPool(runnable -> {
Thread thread = new Thread(runnable, "pty-reaper");
thread.setDaemon(true);
return thread;
});
static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*"); static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*");
static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) LINKER.canonicalLayouts().get("short"); static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) LINKER.canonicalLayouts().get("short");
@@ -275,14 +282,43 @@ public final class LinuxPty implements AutoCloseable {
@Override @Override
public void close() { public void close() {
if (closed) { if (!markClosed()) {
return; return;
} }
closeMaster();
try {
reap();
} finally {
arena.close();
}
}
/** Send the configured close signal and close the master fd now; reap off the caller thread. */
public void closeDetached() {
if (!markClosed()) {
return;
}
closeMaster();
REAPER.submit(() -> {
try {
reap();
} finally {
arena.close();
}
});
}
private synchronized boolean markClosed() {
if (closed) {
return false;
}
closed = true; closed = true;
return true;
}
private void closeMaster() {
callKill(pid, closeSignal); callKill(pid, closeSignal);
callInt(CLOSE, masterFd); callInt(CLOSE, masterFd);
reap();
arena.close();
} }
private void reap() { private void reap() {

View File

@@ -180,8 +180,27 @@ public final class ShellSession implements AutoCloseable {
@Override @Override
public void close() { public void close() {
closed = true; if (!markClosed()) {
return;
}
reader.shutdownNow(); reader.shutdownNow();
pty.close(); pty.close();
} }
/** Signal and disconnect the pty immediately, but leave child reaping to a background thread. */
public void closeDetached() {
if (!markClosed()) {
return;
}
reader.shutdownNow();
pty.closeDetached();
}
private synchronized boolean markClosed() {
if (closed) {
return false;
}
closed = true;
return true;
}
} }

View File

@@ -383,7 +383,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
@Override @Override
public void close() { public void close() {
if (session != null) { if (session != null) {
session.close(); session.closeDetached();
session = null; session = null;
} }
mouseEncoder.close(); mouseEncoder.close();

View File

@@ -86,7 +86,7 @@ final class TerminalWindow {
/** /**
* Fully tears this window down (FX thread, idempotent): stops rendering, closes the compositor — * Fully tears this window down (FX thread, idempotent): stops rendering, closes the compositor —
* which reaps the pane shells via the configured {@code close_signal} — disposes the Stage, and * which signals pane shells via the configured {@code close_signal} — disposes the Stage, and
* notifies the manager so it can drop the window (and, in standalone mode, exit the JVM). Both * notifies the manager so it can drop the window (and, in standalone mode, exit the JVM). Both
* the WM close button and the last-pane-closed hook route through here. * the WM close button and the last-pane-closed hook route through here.
*/ */