Skip to content

Stage a viewer's queue before a Windows update stops it - #1012

Merged
SimonCropp merged 1 commit into
mainfrom
stage-before-update
Oct 31, 2026
Merged

SimonCropp merged 1 commit into
mainfrom
stage-before-update

Conversation

@SimonCropp

Copy link
Copy Markdown
Member

On Windows, the tray's Update stops whatever runs from the tray's store before it updates, since a file in use can't be replaced. That includes a viewer started from the tray's own viewer folder. A viewer that held the inline queue holds it in memory and stages it only as it exits, so being stopped lost every snapshot in it. The docs said to accept or close what was pending before updating.

The change.

  • ListenerTable.Owner(port) returns the id of the process listening on a port. It comes from the same row of Windows' listener table that ListenerTable already reads, which ends with the owning process's id in both layouts. It returns null off Windows, or wherever the table can't be read.
  • TrayUpdate.Start, once the update's shell has started and before the tray exits, calls TrayUpdate.CloseOwningViewer. That:
    • finds whoever listens on the viewer port;
    • passes over this tray itself, whose own queue it stages as it exits;
    • checks whether that process's image is somewhere the update stops (TrayUpdate.Stops: under the store or AlsoStopUnder, ignoring case, by whole folders);
    • if it is, sends it Quit and waits up to fifteen seconds for it to go. The shell waits thirty for the tray.
  • No protocol change. A Quit from the wire already makes a viewer that owns the queue exit, whatever a tray says, and the viewer stages its queue on the way out. Only a close by hand hides the window beside a tray.
  • A viewer from anywhere else, such as DiffEngine's own bundle, is left alone, since nothing stops it.
  • Docs: the tray page's Update section and CLAUDE.md say what happens now.

Tests.

  • ListenerTableTests.AListenerSaysWhoseItIs: this process, for a listener of either family, and null once it stops.
  • TrayUpdateTests.TheUpdateStopsWhatRunsFromTheStore: the store and AlsoStopUnder match, whatever the case, and a sibling folder that only starts with the same name doesn't.
  • TrayUpdateTests.AViewerTheUpdateStopsIsAskedToClose: Windows PowerShell stands in for the viewer, listening on a port of its own, and the update is told to stop under PowerShell's folder. The stand-in is found by the table, sent verb: quit and has exited.
  • TrayUpdateTests.AViewerTheUpdateLeavesAloneIsLeftAlone and AQueueThisTrayHoldsIsNotAskedToClose: nothing is sent.
  • ViewerProgramTests.AQuitFromTheWireClosesBesideATray: with the tray's mutex held and a snapshot queued, a Close from the wire ends the loop without a frame.

Locally on Windows, a Release build had no errors, and:

  • DiffEngineTray.Core.Tests passed 321, with 4 skipped.
  • DiffEngineTray.Tests passed 106.
  • DiffEngineViewer.Tests passed 871, with 25 skipped.
  • DiffEngine.Tests passed 2,267, with 20 skipped. The 3 that failed are the ListenerTable.Enumerated ones that always fail on Windows 10.0.26300, where .NET leaves listeners out of its list; CI passes them.

Not seen working. I didn't run a real update against a real viewer holding a queue.

On Windows the update's shell stops whatever runs from the tray's store
before it updates, and that takes in a viewer started from the tray's
own viewer folder. One that held the inline queue held it in memory and
stages it only as it exits, so its snapshots went with it.

Once the shell has started, the tray now finds the process listening on
the viewer port, out of the same row of Windows' listener table that
ListenerTable already reads, and if it runs from somewhere the update
stops, sends it Quit and waits up to fifteen seconds for it to go. An
owning viewer takes a Quit from the wire as exiting whatever a tray
says, and stages its queue on the way out. A viewer from anywhere else
is left alone, since nothing stops it, and so is a queue the tray holds
itself, which it stages as it exits.
@SimonCropp
SimonCropp merged commit 4ae1a17 into main Oct 31, 2026
15 checks passed
@SimonCropp
SimonCropp deleted the stage-before-update branch October 31, 2026 12:13
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