Skip to content

Apply staged upgrade when mount processes exit#1976

Draft
tyrielv wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/fix-unmountall-upgrade-check
Draft

Apply staged upgrade when mount processes exit#1976
tyrielv wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/fix-unmountall-upgrade-check

Conversation

@tyrielv
Copy link
Copy Markdown
Contributor

@tyrielv tyrielv commented May 15, 2026

Problem

When the installer runs with mounts active, it stages new files to
PendingUpgrade\. The service applies this upgrade when all mounts are
gone — but the trigger relied on UnregisterRepoRequest, which only
gvfs unmount sends.

gvfs service --unmount-all sets SkipUnregister=true (to preserve
automount registration), so no message reached the service and staged
upgrades sat in PendingUpgrade\ indefinitely until the next service
restart.

Solution: Process Exit Monitor

The installer always replaces GVFS.Service.exe in-place (even during
staging), so the running service is always the new version. Instead of
requiring the client gvfs.exe (which may be an old version) to send a
new message, the service proactively monitors mount process exits:

  • PendingUpgradeMonitor — registers Process.Exited callbacks on
    all GVFS.Mount processes. When one exits, a debounced timer (5s)
    fires and calls TryApplyPendingUpgrade. If new mounts started, it
    re-registers. When the upgrade completes or no pending upgrade remains,
    the monitor stops itself. Zero polling cost — purely event-driven.

  • PendingUpgradeHandler — now returns an UpgradeResult enum
    (NoPending, Applied, DeferredMountsRunning, NotReady,
    Failed) so callers can react. Adds a static lock to prevent
    concurrent apply attempts from startup, RequestHandler, and monitor.

  • GVFSService — starts the monitor when the startup upgrade check
    is deferred due to running mounts. Disposes on shutdown.

The existing RequestHandler unmount trigger is preserved as
belt-and-suspenders for the normal gvfs unmount path.

Test

New unmount-all-triggers-upgrade scenario in the CI upgrade test
matrix. Installs LKG, mounts, stages new version, uses --unmount-all
(which runs the old LKG gvfs.exe), and verifies PendingUpgrade\
is consumed by the monitor without a service restart.

The installer replaces GVFS.Service.exe in-place even during a staged
upgrade, so the running service is always the new version. Add a
PendingUpgradeMonitor that registers Process.Exited callbacks on
GVFS.Mount processes and applies the staged upgrade once all have
exited. This is event-driven (no polling) and works regardless of
which version of gvfs.exe triggered the unmount.

Previously only 'gvfs unmount' triggered the upgrade check (via
UnregisterRepoRequest). 'gvfs service --unmount-all' sets
SkipUnregister=true to preserve automount registration, so no message
reached the service and staged upgrades sat in PendingUpgrade\
indefinitely.

Changes:
- PendingUpgradeHandler: return UpgradeResult enum instead of void,
  add concurrency lock, add IsPending() helper
- PendingUpgradeMonitor: new class — monitors mount process exits,
  debounces (5s), retries on new mounts, self-stops on completion
- GVFSService: start monitor when startup upgrade is deferred,
  dispose on shutdown
- upgrade-tests.yaml: add unmount-all-triggers-upgrade CI scenario

Assisted-by: Claude Opus 4.6
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
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.

1 participant