DevelopmentAll versions
On this page

Pty4j Local Process Lifecycle & Watcher Threads

The local PTY connector adapts a Pty4J process to the ordered raw-byte TerminalConnector contract. Session construction and consumer examples are in the README. This guide describes connector lifecycle and the native integration seam.

1. Threading Architecture

Creating the connector launches its child process. Calling start(listener) starts two daemon threads; it permits one start and rejects a closed connector.

Worker Default name Work
Reader terminal-pty-reader Read stdout and synchronously invoke onBytes in stream order.
Watcher terminal-pty-watcher Wait for process exit, then wait for stdout delivery to finish before reporting normal closure.

The reader allocates one byte buffer at startup, by default 8,192 bytes, and reuses it. The listener borrows only the supplied range for the duration of onBytes; copy data that must outlive the callback. Chunks need not align with UTF-8 scalars, escape sequences, or terminal frames. The session processes them synchronously and owns parsing.

The watcher can observe process exit before the final read. It waits for EOF and completed byte callbacks before onClosed(exitCode). Reader EOF alone does not report closure while the process is still running. PtyConnector.waitFor() waits only for process exit; it is not an output-drain boundary.

Daemon workers do not keep the JVM alive. Hosts must still close their sessions to release native resources.

Failure and local close

A reader or byte-listener exception is retained in PtyConnector.failure and reported through onError. The connector closes the process and both streams, then invokes onClosed(null). It does not retry the failed chunk or replace the original failure with a later process exit. Connector callbacks may close reentrantly.

Local close cancels pending delivery rather than draining it. It does not emit a remote onClosed callback; the session records its own local termination. Exceptions arriving after local close are teardown and do not become remote failures.

Cleanup requests process destruction and closes both streams without waiting for an active byte callback. The reader retains its borrowed buffer until that callback returns, then stops delivery; the watcher exits after the reader. Close is idempotent, but native destruction and stream cleanup can block. Keep it off a UI thread.

Writes and resize

Direct connector writes validate the byte range, serialize access to stdin, and flush the supplied range before returning. Closed connectors ignore valid writes. The session's outbound writer orders user input and terminal replies; do not write through a retained connector after transferring it to a session.

Resize requires positive dimensions and synchronously delegates to WinSize(columns, rows) while the connector is open. Use session resize APIs for a session-owned connector so grid reflow, transport dimensions, and viewport metadata are coordinated.

2. Windows ConPTY Considerations

The factory enables Pty4J's ConPTY option. Platform process creation and native library loading are owned by Pty4J; startup may fail when the local native environment or requested executable is unavailable. Initial dimensions are supplied during process creation and propagated again before session delivery starts.

The pinned native backend has known pixel-size and graphics cursor constraints. See backend limitations for causes, reproduction evidence, and upstream references.

Foreground-process lookup uses the Unix terminal's foreground process group when available. Windows selects the newest live descendant, which can be a background child. Missing permissions, unavailable metadata, closure, or an oversized descendant tree can yield no name. Applications inside SSH or WSL are not inspected. Treat this information as presentation metadata rather than a process-control guarantee.

Native validation

Ordinary module tests use fake process streams and do not require a native shell. PtyRealProcessTest is skipped unless terminal.pty.host=true:

./gradlew :ketraterm-pty:test --tests "io.github.ketraterm.pty.PtyRealProcessTest" "-Dterminal.pty.host=true"

The native suite requires working Pty4J support and a local shell: /bin/sh on Unix, or cmd.exe and powershell.exe on Windows. Its OSC 52 byte harness also requires node on PATH and the repository's tools/osc52/osc52.mjs. Tests use explicit output/readiness signals and deadlines; skipped native tests do not establish platform support for the current environment.