Skip to content

Ask the listener table whether a viewer holds the queue - #999

Merged
SimonCropp merged 1 commit into
mainfrom
remote-host-listener-table
Oct 29, 2026
Merged

SimonCropp merged 1 commit into
mainfrom
remote-host-listener-table

Conversation

@SimonCropp

Copy link
Copy Markdown
Member

The tray takes a viewer that owns the inline queue for one that has gone, on Windows 10.0.26300. Found because sixteen tray core tests failed on that machine and passed on CI.

Cause. RemoteInlineHost drives a queue a viewer owns. Before each exchange it asked .NET's IPGlobalProperties.GetActiveTcpListeners() whether anything held the viewer port, and skipped the connect when nothing did. That list is every TCP row from GetTcpTable, with the rows in the LISTEN state picked out.

On Windows 10.0.26300, GetTcpTable gives a listening socket's row a dwState of 0, which isn't a TCP state at all, so .NET reads it as Unknown. What I saw on that build:

  • A plain TcpListener on loopback has dwState 0, and .NET reports Unknown for it.
  • netstat -ano doesn't show it, and shows the running tray's listener on 3492 as CLOSED.
  • GetExtendedTcpTable with the listener class lists it.

So the host said nobody held the port while a viewer did. With no connect made, the listing was empty and the owner counted as absent. The tray showed none of the viewer's snapshots, and an accept-all read the queue as empty. The failing tests included AnOwnerThatDoesNotAnswerHoldsTheDeletes.

Fix. Ask ListenerTable instead, which is what ViewerClient and PiperClient already ask. On Windows it asks for the listeners alone, which that build lists correctly. A table that can't be read still counts as held, so the connect decides, which is what the old null meant.

Tests.

  • TrackerSnapshotTest.TheOwnerIsLookedForInTheListenerTable: the host asks the table, and finds the fake viewer.
  • TrackerSnapshotTest.NobodyInTheListenerTableIsNoOwner: a table saying nobody holds the port is believed, and the viewer is never connected to.
  • With main's RemoteInlineHost, in a throwaway worktree, the first fails on that machine. The second passes there, because .NET's list already says nobody, but would fail on a correct Windows, where the old code connects.
  • On that machine: the tray core tests go from 16 failed to 0 of 298, and the Windows head's 112 pass.

RemoteInlineHost asked .NET's GetActiveTcpListeners whether anything
held the viewer port, and skipped the connect when it said nothing did.
Windows 10.0.26300 gives a listening socket's row in the table that list
is read from a state of 0, which .NET reads as Unknown, so every listener
an app opened was left out: a TcpListener's row there has dwState 0, and
netstat shows the tray's own listener as CLOSED. On that build the tray
took a viewer that held the queue for one that had gone, showed none of
its snapshots, and in an accept-all would read the queue as empty. That
failed sixteen tray core tests locally, among them the one that holds a
delete back while the owner cannot be asked, and none on CI.

It now asks ListenerTable, as ViewerClient and PiperClient do, which on
Windows asks for the listeners alone and lists them on that build. A
table that cannot be read still leaves the connect to decide.

Two tests pin it: the host asks the table, and a table saying nobody
holds the port is believed with no connect.
@SimonCropp
SimonCropp merged commit 6f91a33 into main Oct 29, 2026
15 checks passed
@SimonCropp
SimonCropp deleted the remote-host-listener-table branch October 29, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant