Skip to content

supervise: tick() holds s.mu across an unbounded engine probe; one hung COM call wedges the whole bridge #437

Description

@zcsizmadia

internal/supervise/supervise.go holds s.mu across Engine.Running, and nothing on that path has a deadline.

Path: tickengineAdapter.RunningProvisioner.engineRunningFast.ListcomSession.do(ctx). The ctx is the supervisor's process-lifetime context — there is no per-tick timeout anywhere on it.

If EnumerateDistributions hangs — a service restart, wsl --update, a wedged VM — do blocks on <-done until shutdown while holding s.mu, and then:

  • every Demand() blocks, so every docker connection through demandDialer hangs rather than failing — a hang is worse than an error here, because the CLI just sits there
  • LifecycleSnapshot blocks, so the stats file freezes and the tray cannot even show that the supervisor is stuck
  • the poke loop never runs again (same goroutine)

The COM rewrite amplified this

Before #380, COM callers were independent processes shelling out to wsl.exe. Now every caller — List, Terminate, doctor — queues behind one unbuffered channel and one OS thread (the apartment goroutine). A single hung call stalls callers that used to be unable to affect each other.

Related, same file: a panic on the apartment thread has no recover, so it takes the whole supervisor down — bridge and reconciler included. unsafe.Slice(arr, count) panics if the service ever returns count > 0 with a nil array, and hr == 0 is the only guard.

Fix

  • A per-tick deadline on the engine probe, sized so a slow-but-alive wsl call still succeeds. The supervisor's own ctx is the wrong bound for a single tick.
  • Do not hold s.mu across it. The probe's result is what needs the lock, not the probe.
  • recover on the apartment goroutine, and restart the loop rather than leaving it dead — a dead loop with s.stop unclosed turns every later do into a block until its ctx fires, which on this path is never.

Found in the pre-0.6.0 independent concurrency review.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions