Skip to content

Add port_in_use and exit_if_port_in_use to ls_zprocess - #140

Closed
chrisjbillington wants to merge 1 commit into
masterfrom
port-in-use
Closed

chrisjbillington wants to merge 1 commit into
masterfrom
port-in-use

Conversation

@chrisjbillington

@chrisjbillington chrisjbillington commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Written by Claude. This comment and the commit it describes were produced by
Claude, the AI model, in a Claude Code session that @chrisjbillington is running
for labscript suite maintenance. It is posted from his account; the commit is
authored as Claude.

If you start a second copy of BLACS, lyse, runmanager or runviewer while one is already running, the second copy fails with a ZMQError: Address already in use traceback when it binds its port. Depending on the application, it gets there only after doing a lot of start-up work. lyse has created its routine boxes by then, and a second BLACS (once blacs #138 is merged) would have created its device tabs and started their workers, which open the same hardware as the running copy.

This PR adds two functions to labscript_utils.ls_zprocess, so that the applications can check their port early in start-up:

  • port_in_use(port) attempts a TCP connection to the port on 127.0.0.1 and closes it without sending anything. The ZMQ server on the other end never receives a message.
  • exit_if_port_in_use(app_name, port, splash) logs an error and shows it in a message box saying that another copy is probably running, then exits.

The Windows part needs some explanation. By default, Windows retries a connection to a localhost port that has no listener, so the attempt takes about a second to fail. A socket timeout avoids the wait but adds about 15 ms even when the connection succeeds. port_in_use therefore uses the SIO_TCP_INITIAL_RTO ioctl through ctypes to turn off the retries on that socket, and makes the connection attempt without a timeout. With the retries off, Windows accepts or refuses a localhost connection at once.

Testing:

  • On GitHub's Windows Server 2022 and 2025, macOS and Ubuntu runners, with Python 3.11 and 3.14, I made 200 calls each on a free port, on a ZMQ server port and on a plain listening socket. Every call gave the right result. On Windows the median call took 0.04 to 0.18 ms, compared with 100 to 110 ms for a plain connection attempt with a 0.1 s timeout. On macOS and Ubuntu every median was under 0.1 ms.
  • I ran an end-to-end test on Ubuntu and Windows for Python 3.11 to 3.14 with PySide6, PyQt6 and PyQt5, with this branch and the branches that use it (blacs Set the default zprocess ProcessTree with set_default_process_tree() (needs zprocess 3)聽#138 and the lyse, runmanager and runviewer PRs). The test starts each application, calls port_in_use on its port and on a free port, and starts a second copy. Every call gave the right result, at most 1.0 ms on Windows. Every second copy reported that its port was in use within 1 to 3 s, and the second BLACS created no device tabs (run).

馃 Generated with Claude Code

https://claude.ai/code/session_01JHLgS3hh2FdpwGHFUTji9y

port_in_use(port) attempts a connection to 127.0.0.1 and closes it
without sending anything. On Windows it disables SYN retransmission on
the socket, so that a refused connection fails at once instead of after
about a second, and makes the attempt without a socket timeout, which
adds about 15 ms. exit_if_port_in_use(app_name, port, splash) logs and
shows an error and exits if the port is in use, for the applications to
call at start-up.

On GitHub's Windows, macOS and Ubuntu runners, with Python 3.11 and 3.14,
200 calls each on a free port, a ZMQ server port and a plain listening
socket all returned the right result. Windows calls took a median of
0.04 to 0.18 ms; Ubuntu and macOS took under 0.1 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHLgS3hh2FdpwGHFUTji9y
@chrisjbillington chrisjbillington changed the title Add labscript_utils.ports for checking whether a local port is in use Add port_in_use and exit_if_port_in_use to ls_zprocess Oct 24, 2026
@chrisjbillington

Copy link
Copy Markdown
Member Author

Written by Claude. This comment was produced by Claude, the AI model, in a Claude
Code session that @chrisjbillington is running for labscript suite maintenance. It is
posted from his account.

I'm closing this in favour of chrisjbillington/zprocess#41. port_in_use is a plain socket check that doesn't use the labconfig, so it now goes in zprocess.utils. The message box that exit_if_port_in_use showed is now in each application, which already has a QApplication, a splash screen and a logger at that point, so labscript-utils no longer needs to import Qt inside a function. labscript-suite/blacs#138, labscript-suite/lyse#159, labscript-suite/runmanager#146 and labscript-suite/runviewer#80 are updated to call zprocess.port_in_use.

@chrisjbillington
chrisjbillington deleted the port-in-use branch October 25, 2026 00:40
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.

2 participants