Skip to content

wsl2: provisionVM goroutine hot-loops on for { <-ctx.Done() } after instance stop #4891

@mn-ram

Description

@mn-ram

Description

The boot-script goroutine spawned by provisionVM in pkg/driver/wsl2/vm_windows.go (lines 116–143) wraps a one-shot context-cancellation wait in an infinite for {} loop:

go func() {
    cmd := exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "bash", "-c", limaBootFileLinuxPath)
    out, err := cmd.CombinedOutput()
    os.RemoveAll(limaBootFileWinPath)
    ...
    for {
        <-ctx.Done()
        logrus.Info("Context closed, stopping vm")
        if status, err := getWslStatus(ctx, instanceName); err == nil &&
            status == limatype.StatusRunning {
            _ = stopVM(ctx, distroName)
        }
    }
}()

Once ctx is canceled (the normal trigger for any limactl stop or hostagent shutdown), <-ctx.Done() returns instantly on every iteration. The goroutine then hot-loops:

  • pinning a CPU core,
  • spawning a fresh wsl.exe subprocess on every iteration via getWslStatus (and, until the VM shows as stopped, via stopVM too),
  • flooding wslservice.exe, which is global on the Windows host and shared with every other WSL distro (Docker Desktop, Rancher Desktop, plain Ubuntu shells, etc.).

The hostagent process never exits cleanly because this goroutine never returns; ha.pid / ha.sock linger until the OS kills the hostagent.

Reproduction (deterministic)

limactl start --vm-type=wsl2 --name=demo template://default-windows
# ... wait for ready ...
limactl stop demo

Within ~1 second of the stop returning, the hostagent process pegs a CPU core and wsl.exe PIDs increment many times per second (visible in Process Explorer or Get-Process wsl polled in PowerShell). The hostagent does not exit on its own.

This is structural, not timing-dependent — the loop has no exit condition.

Fix

The intent was clearly "wait once, then stop the VM." Dropping the for { /} wrapper restores that. PR follows.

Environment

Affects all WSL2 instances (vmType: wsl2) on Windows. Found by code inspection against master.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions