A second StemDeck no longer adopts the first one's backend - #431
Merged
Conversation
Launching a second StemDeck while one was running gave the new window the already-running instance's backend, and with it that instance's data directory and library. Nothing on screen suggested anything was wrong. Two independent faults had to line up, so both are fixed. The port reservation probed 127.0.0.1 while the backend binds 0.0.0.0. On Windows those do not collide, so an occupied port looked free, the fallback to another port never ran, and the backend we spawned died on bind with 10048. The reservation now claims the same address the backend will bind, so a taken port reads as taken. It claims it without listening. bind is what reserves an address; listen is what makes a program a server, and a server on 0.0.0.0 is what makes Windows Firewall interrupt the user. The shell should not be answering that prompt on the backend's behalf, so the reservation binds only. The health check accepted any 200 on the port. That is what turned a dead child into a silent adoption: the other instance answered instantly while ours was still starting. /api/health now reports the answering process, and only the child we spawned is accepted. The child is watched while polling too, so a backend that cannot bind fails in a second with a message naming the contended port, rather than after ninety with a stack trace. Verified by removing the identity check and confirming the scenario test fails without it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #424.
Launching a second StemDeck while one was already running handed the new window the already-running instance's backend, and with it that instance's data directory and library. Nothing on screen suggested anything was wrong.
Two independent faults had to line up, so both are fixed.
The port reservation probed the wrong address
reserve_portbound127.0.0.1while the backend binds0.0.0.0. On Windows those do not collide, so an occupied port looked free, the fallback to another port never ran, and the backend we spawned died on bind:The reservation now claims the same address the backend will bind, so a taken port reads as taken and the fallback does its job.
It claims the port without listening on it
bindis what reserves an address;listenis what makes a program a server. Reserving with aTcpListeneron0.0.0.0would make StemDeck.exe itself a server in Windows Firewall's eyes and prompt every user on next launch, on top of the prompt the backend already causes. The shell should not be answering that prompt on the backend's behalf, so the reservation binds only, viasocket2(already in the dependency graph, so no new crate is pulled in; the lock diff is one line).A test pins the assumption this rests on: a held, non-listening reservation still refuses a real listener, on both Windows and Linux.
The health check accepted any 200
This is what turned a dead child into a silent adoption. The other instance answered instantly while ours was still importing torch, so the very first poll succeeded and the shell pointed the window at a backend that was never ours.
/api/healthnow reports the answering process, andwait_for_healthaccepts only the child it spawned. The child is watched while polling too, so a backend that cannot bind its port fails in about a second with a message naming the contended port, instead of after ninety with a stack trace to interpret.Testing
a_stranger_on_the_port_is_never_accepted_as_our_backendstands up a fake instance answering 200 on the port while the spawned child dies, exactly as reported. It must return an error naming the port.our_own_backend_is_acceptedcovers the other direction, so verification cannot be so strict that a healthy start is refused.main. Ruff clean.Behaviour change worth noting
A second instance now starts its own backend on a different port rather than failing or adopting. Two installs can run side by side, each with its own library, which is what someone launching a second copy expects.