Skip to content

Split ServiceVerb repo-state check into intent-specific predicates - #2058

Merged
tyrielv merged 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/fix-service-verb-mount-status
Jul 14, 2026
Merged

Split ServiceVerb repo-state check into intent-specific predicates#2058
tyrielv merged 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/fix-service-verb-mount-status

Conversation

@tyrielv

@tyrielv tyrielv commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

gvfs service --unmount-all intermittently fails with exit code 3 and Already unmounting, please wait, surfacing as the flaky functional test GVFS.FunctionalTests.Tests.MultiEnlistmentTests.ServiceVerbTests.ServiceCommandsWithNoRepos.

Root cause

The service verbs (--list-mounted, --mount-all, --unmount-all) iterate the service's global repo registry and used a single IsRepoMounted(repoRoot) helper. That helper returned true whenever the mount named pipe merely answered a GetStatus request — regardless of the actual MountState. StatusVerb reports success as long as it can connect, so a GVFS.Mount process in the transient Unmounting state (a concurrent or prior unmount still shutting it down) counts as "mounted."

--unmount-all therefore attempts to unmount a repo that is already on its way out, gets the transient Already unmounting response from the mount process, and treats it as a hard failure — even though the desired end state (repo unmounted) is already being reached.

Fix

Replace IsRepoMounted with three intent-specific predicates, all built on a TryGetRepoMountStatus primitive that reads the actual MountState:

Predicate Meaning Caller
IsRepoReady MountState == Ready --list-mounted
IsRepoAvailableToMount no live mount process answering --mount-all
IsRepoAvailableToUnmount MountState ∈ {Ready, MountFailed} --unmount-all

--unmount-all now skips repos that are Unmounting (already reaching the desired state) or Mounting (an unmount request would be rejected anyway), eliminating the spurious failure.

StatusVerb exposes the "Mount status: " output prefix as a shared constant so ServiceVerb recovers the MountState without re-implementing the pipe protocol (and without changing StatusVerb's worktree/enlistment resolution, so there's no behavior regression for worktree mounts).

Behavior changes

  • --unmount-all: skips repos in Unmounting/Mounting instead of failing on them. (The fix.)
  • --list-mounted: now reports only fully-Ready repos (previously also listed Mounting/Unmounting/MountFailed processes that happened to answer the pipe).
  • --mount-all: unchanged — still mounts only when no live mount process is answering.

Testing

  • Builds clean (dotnet build GVFS\GVFS\GVFS.csproj -c Debug, 0 warnings).
  • Covered by the existing ServiceVerbTests functional suite (ServiceCommandsWithNoRepos, ServiceCommandsWithMultipleRepos, ServiceCommandsWithMountAndUnmount), whose expectations remain consistent with the new state-aware behavior. There is no unit-test seam for the service verbs (they require a live service pipe).

Related

This is the first of a set of fixes for the same flaky test. Two follow-ups are tracked separately: closing the mount-process lifecycle gap (a repo committed to unmounting still presents as a normal mounted repo to concurrent callers), and scoping the functional-test service registry so the global --mount-all/--unmount-all verbs can't reach across parallel fixtures.

`gvfs service --unmount-all` intermittently failed with exit code 3 and
"Already unmounting, please wait" when the service registry still listed a
repo whose GVFS.Mount process was in the transient Unmounting state (a
concurrent or prior unmount still shutting it down). The single IsRepoMounted
helper returned true whenever the mount pipe merely answered a GetStatus
request, regardless of the actual MountState, so --unmount-all would attempt
to unmount a repo already on its way out and treat that transient state as a
hard failure.

Replace IsRepoMounted with three intent-specific predicates built on a
TryGetRepoMountStatus primitive that reads the actual MountState:
  - IsRepoReady (Ready)                          -> --list-mounted
  - IsRepoAvailableToMount (no live process)     -> --mount-all
  - IsRepoAvailableToUnmount (Ready|MountFailed) -> --unmount-all

--unmount-all now skips repos that are Unmounting (already reaching the
desired state) or Mounting (an unmount request would be rejected anyway),
eliminating the spurious failure. --list-mounted now reports only fully-Ready
repos. --mount-all behavior is unchanged (it still mounts only when no live
mount process is answering).

StatusVerb exposes the "Mount status: " output prefix as a shared constant so
ServiceVerb recovers the MountState without re-implementing the pipe protocol.

Fixes the flaky
GVFS.FunctionalTests.Tests.MultiEnlistmentTests.ServiceVerbTests.ServiceCommandsWithNoRepos.

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv
tyrielv marked this pull request as ready for review July 10, 2026 17:26
@tyrielv
tyrielv enabled auto-merge July 10, 2026 17:26
Comment thread GVFS/GVFS/CommandLine/ServiceVerb.cs
@tyrielv
tyrielv merged commit e02c281 into microsoft:master Jul 14, 2026
35 checks passed
@tyrielv tyrielv mentioned this pull request Jul 15, 2026
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