Skip to content

Packaged preindexed source read opens are serialized behind an exclusive mutex under concurrent winget usage #6167

@mamoreau-devolutions

Description

@mamoreau-devolutions

Brief description of your issue

Packaged winget appears to serialize read-only opens of the preindexed winget source behind a single exclusive cross-process mutex. Under concurrent winget usage, commands that should be quick source reads can stall for tens of seconds or longer before doing meaningful work.

Steps to reproduce

Run several concurrent show commands and print elapsed time for each job:

$w = (Get-Command winget.exe).Source

1..6 | ForEach-Object {
    $i = $_
    Start-Job -ArgumentList $i, $w {
        param($i, $w)
        $s = [Diagnostics.Stopwatch]::StartNew()
        & $w show --id Devolutions.RemoteDesktopManager --exact --source winget --disable-interactivity --accept-source-agreements *> $null
        [pscustomobject]@{
            Job      = $i
            Seconds  = [math]::Round($s.Elapsed.TotalSeconds, 3)
            ExitCode = $LASTEXITCODE
        }
    }
} | Wait-Job | Receive-Job | Sort-Object Job | Format-Table -AutoSize

Sample run

Job Seconds ExitCode
--- ------- --------
  1   44.84        0
  2  163.93        0
  3  122.67        0
  4  204.52        0
  5   86.86        0
  6  241.91        0

All six commands succeeded, but some waited several minutes.

Expected behavior

Read-only commands such as winget show and winget search should not block behind unrelated concurrent winget activity when they only need to open and read the packaged source index.

Actual behavior

The command often appears idle for a long time, then completes quickly once source open proceeds.

Analysis

The relevant path appears to be in:

  • src\AppInstallerRepositoryCore\Microsoft\PreIndexedPackageSourceFactory.cpp
  • src\AppInstallerCommonCore\Synchronization.cpp
  • src\AppInstallerRepositoryCore\RepositorySource.cpp

Observed behavior suggests that read-only packaged source open uses the same named cross-process mutex as source update/add/remove operations:

PreIndexedSourceCPL_Microsoft.Winget.Source_8wekyb3d8bbwe

In instrumented runs, the expensive part was the wait to acquire that mutex, not the actual source read work. Representative timings from local investigation:

  • SQLite search: ~3-5 ms
  • warm package version data load: ~6-10 ms
  • warm manifest load: ~12-18 ms
  • packaged source open once it proceeds: ~80-130 ms
  • mutex wait in contended runs: ~10 s, ~26 s, and higher in multi-process runs

This creates a convoy effect:

  1. one process acquires the preindexed source mutex
  2. other processes queue behind it, including read-only commands
  3. the waiting time dominates wall-clock duration even though their own source read work is small

One detail that stood out while reading the code is that comments refer to a reader-writer lock, while the current implementation appears to use a plain exclusive mutex.

Impact

This likely affects more than winget show. Any command that opens the packaged winget source may be impacted, including:

  • winget search
  • winget show
  • winget install during package lookup / manifest resolution
  • winget upgrade during lookup / manifest resolution
  • winget download

For scripts or tools that invoke winget concurrently like UniGetUI, or just when developing winget locally with multiple AI agents running winget commands concurrently, this can cause highly variable latency and very large slowdowns in otherwise read-mostly workflows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-PerformanceIssue related to CPU or memory performanceIssue-FeatureThis is a feature request for the Windows Package Manager client.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions