Description
Closing the × next to a Local terminal tab removes the tab, but a foreground OpenOCD process remains alive and keeps its TCP ports bound. Starting OpenOCD again in a new terminal then fails with Address already in use.
This can interrupt a normal troubleshooting workflow: close the terminal session to restart a debug server, open a new terminal, and run the same command again. The previous tab is gone, but its server still prevents the next instance from starting. The reproduction uses a single server and requires no connected hardware.
Environment
- macOS 26.6.2 (25G83), Apple Silicon / arm64
- OneWare Studio 1.0.31, unmodified source at
a7c83b069ee3a696e243b085b341e9be620f2bc6
- .NET SDK 10.0.300; terminal shell:
/bin/zsh
- OpenOCD 0.12.0
- Fresh OneWare profile, no custom extensions or application dependencies
Steps to reproduce
-
With TCP ports 6666 and 4444 available, save the following as minimal.cfg:
bindto 127.0.0.1
tcl_port 6666
telnet_port 4444
gdb_port disabled
noinit
noinit lets OpenOCD wait for commands without initializing a debug adapter or target. This reproduction exercises its real Tcl/Telnet servers; it does not require a board.
-
In OneWare's Local terminal, run it in the foreground:
openocd -f /absolute/path/to/minimal.cfg
-
Wait for the messages that OpenOCD is listening on ports 6666 and 4444.
-
Close the individual Local tab using its ×, while OpenOCD is still running. Keep OneWare open.
-
Wait six seconds, open a new terminal, and run the same command again.
Expected behavior
Closing the terminal session should stop its foreground server and release the ports so the server can be started again. If confirmation is required for a running command, it should be requested before discarding the session.
Actual behavior and test results
The tab closes without a confirmation prompt. The original OpenOCD process is still listening six seconds later, and the new instance exits with code 1:
Error: couldn't bind tcl to socket on port 6666: Address already in use
Each row below used a fresh OneWare profile and process. Each run started with both ports available.
| Action |
Original server after closing the tab |
Same command in a new OneWare terminal |
| Local tab ×, run 1 |
PID 19416 still listening on 6666 and 4444 at +6 s |
Exit 1, Address already in use |
| Local tab ×, run 2 |
PID 19542 still listening on 6666 and 4444 at +6 s |
Exit 1, Address already in use |
| Ctrl+C first, then Local tab × |
Original PID 19479 exited; both ports released |
New PID 19522 started and listened on both ports |
The control run's new terminal output was:
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
lsof also confirmed that the original OpenOCD PID owned both listening sockets after closing the tab. No &, nohup, daemon mode, or deliberate detachment was used.
Observed socket ownership six seconds after tab close
Selected columns from lsof -nP -a -p <original OpenOCD PID> -iTCP -sTCP:LISTEN:
Run 1
COMMAND PID NAME
openocd 19416 TCP 127.0.0.1:6666 (LISTEN)
openocd 19416 TCP 127.0.0.1:4444 (LISTEN)
Run 2
COMMAND PID NAME
openocd 19542 TCP 127.0.0.1:6666 (LISTEN)
openocd 19542 TCP 127.0.0.1:4444 (LISTEN)
The original server PID was unchanged across tab close in each run. Test-owned processes were cleaned up after measurement, and both ports were available again after each run.
Related terminal cleanup code
I noticed the AI terminal cancellation improvements in #316. In the tested revision, the automation cancellation path attempts Ctrl+C recovery, then calls KillProcess() and discards the terminal if recovery fails.
The regular Local tab close path goes through Terminal.Close() → Disconnect() → Dispose(). The Unix implementation disposes the streams, but in the macOS reproduction above the foreground server survives that path.
Could you check whether the regular tab-close path also needs explicit cleanup of the session's running processes on macOS? The AI path is included as a source-code reference; the runtime tests above exercise Local tab close, not AI cancellation.
Test method and scope
The results above came from an automated Avalonia Headless run of the original DesktopStudioApp, using the actual Local tab header button's bound command and real OS shell, PTY, and OpenOCD processes. The original application class, built-in modules, services, and terminal implementation were used without overrides. This was a source-build test, not a recording of mouse clicks in a packaged release.
The runtime contained no custom application assemblies; all 167 DLLs copied from the stock OneWare desktop build matched their source-build hashes. The only additional assemblies were the external test driver and Avalonia Headless. Automatic binary downloads were disabled through the normal per-profile setting.
This report concerns the Local session's tab × on macOS. The OpenOCD configuration above tests Tcl/Telnet port retention; GDB/JTAG hardware access and full application shutdown were not tested. Ctrl+C is a workaround for the server/port issue; an idle shell still remained after the control tab was closed.
Description
Closing the × next to a Local terminal tab removes the tab, but a foreground OpenOCD process remains alive and keeps its TCP ports bound. Starting OpenOCD again in a new terminal then fails with
Address already in use.This can interrupt a normal troubleshooting workflow: close the terminal session to restart a debug server, open a new terminal, and run the same command again. The previous tab is gone, but its server still prevents the next instance from starting. The reproduction uses a single server and requires no connected hardware.
Environment
a7c83b069ee3a696e243b085b341e9be620f2bc6/bin/zshSteps to reproduce
With TCP ports 6666 and 4444 available, save the following as
minimal.cfg:noinitlets OpenOCD wait for commands without initializing a debug adapter or target. This reproduction exercises its real Tcl/Telnet servers; it does not require a board.In OneWare's Local terminal, run it in the foreground:
Wait for the messages that OpenOCD is listening on ports 6666 and 4444.
Close the individual Local tab using its ×, while OpenOCD is still running. Keep OneWare open.
Wait six seconds, open a new terminal, and run the same command again.
Expected behavior
Closing the terminal session should stop its foreground server and release the ports so the server can be started again. If confirmation is required for a running command, it should be requested before discarding the session.
Actual behavior and test results
The tab closes without a confirmation prompt. The original OpenOCD process is still listening six seconds later, and the new instance exits with code 1:
Each row below used a fresh OneWare profile and process. Each run started with both ports available.
Address already in useAddress already in useThe control run's new terminal output was:
lsofalso confirmed that the original OpenOCD PID owned both listening sockets after closing the tab. No&,nohup, daemon mode, or deliberate detachment was used.Observed socket ownership six seconds after tab close
Selected columns from
lsof -nP -a -p <original OpenOCD PID> -iTCP -sTCP:LISTEN:The original server PID was unchanged across tab close in each run. Test-owned processes were cleaned up after measurement, and both ports were available again after each run.
Related terminal cleanup code
I noticed the AI terminal cancellation improvements in #316. In the tested revision, the automation cancellation path attempts Ctrl+C recovery, then calls
KillProcess()and discards the terminal if recovery fails.The regular Local tab close path goes through
Terminal.Close()→Disconnect()→Dispose(). The Unix implementation disposes the streams, but in the macOS reproduction above the foreground server survives that path.Could you check whether the regular tab-close path also needs explicit cleanup of the session's running processes on macOS? The AI path is included as a source-code reference; the runtime tests above exercise Local tab close, not AI cancellation.
Test method and scope
The results above came from an automated Avalonia Headless run of the original
DesktopStudioApp, using the actual Local tab header button's bound command and real OS shell, PTY, and OpenOCD processes. The original application class, built-in modules, services, and terminal implementation were used without overrides. This was a source-build test, not a recording of mouse clicks in a packaged release.The runtime contained no custom application assemblies; all 167 DLLs copied from the stock OneWare desktop build matched their source-build hashes. The only additional assemblies were the external test driver and Avalonia Headless. Automatic binary downloads were disabled through the normal per-profile setting.
This report concerns the Local session's tab × on macOS. The OpenOCD configuration above tests Tcl/Telnet port retention; GDB/JTAG hardware access and full application shutdown were not tested. Ctrl+C is a workaround for the server/port issue; an idle shell still remained after the control tab was closed.