Skip to content

Commit 3ea5317

Browse files
Add cross-process mutex and retry logic for VS Setup Configuration COM API
The VS Setup Configuration COM API (ISetupConfiguration2) has a known concurrency bug that causes failures (exit code 57005/0xDEAD) when multiple processes enumerate VS instances simultaneously. This hits template engine integration tests that run dotnet new in parallel. The previous fix (PR #44930) added an in-process lock (s_guard) but that doesn't help when multiple test processes call the API concurrently. This fix adds: - A named system mutex (Global\DotNetSdk_VSSetupConfiguration) to serialize cross-process access to the COM API - Retry logic with exponential backoff (3 attempts) as a safety net for cases where external processes also call the API without our mutex Fixes #44878 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dec5a57 commit 3ea5317

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

src/Cli/dotnet/Commands/Workload/List/VisualStudioWorkloads.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@ internal static class VisualStudioWorkloads
2323
{
2424
private static readonly object s_guard = new();
2525

26+
/// <summary>
27+
/// Named mutex used to serialize cross-process access to the VS Setup Configuration COM API,
28+
/// which is not safe for concurrent calls from multiple processes.
29+
/// See https://github.com/dotnet/sdk/issues/44878
30+
/// </summary>
31+
private const string VsSetupConfigurationMutexName = @"Global\DotNetSdk_VSSetupConfiguration";
32+
33+
/// <summary>
34+
/// Maximum number of retry attempts when the COM API fails due to concurrent access.
35+
/// </summary>
36+
private const int MaxRetryAttempts = 3;
37+
38+
/// <summary>
39+
/// Base delay in milliseconds between retry attempts (doubled on each retry).
40+
/// </summary>
41+
private const int RetryBaseDelayMs = 100;
42+
2643
/// <summary>
2744
/// Visual Studio product ID filters. We dont' want to query SKUs such as Server, TeamExplorer, TestAgent
2845
/// TestController and BuildTools.
@@ -105,6 +122,55 @@ internal static unsafe void GetInstalledWorkloads(
105122
IWorkloadResolver workloadResolver,
106123
InstalledWorkloadsCollection installedWorkloads,
107124
SdkFeatureBand? sdkFeatureBand = null)
125+
{
126+
// Use a named mutex to serialize cross-process access to the VS Setup Configuration COM API.
127+
// The API has a known concurrency bug that causes failures (exit code 57005/0xDEAD) when
128+
// multiple processes enumerate VS instances simultaneously.
129+
// See https://github.com/dotnet/sdk/issues/44878 and https://dev.azure.com/devdiv/DevDiv/_workitems/edit/2241752
130+
using var mutex = new Mutex(initiallyOwned: false, VsSetupConfigurationMutexName);
131+
132+
for (int attempt = 0; attempt <= MaxRetryAttempts; attempt++)
133+
{
134+
try
135+
{
136+
// Wait up to 30 seconds to acquire the mutex. If we can't acquire it,
137+
// proceed anyway (best-effort serialization).
138+
bool acquired = false;
139+
try
140+
{
141+
acquired = mutex.WaitOne(TimeSpan.FromSeconds(30));
142+
}
143+
catch (AbandonedMutexException)
144+
{
145+
// Another process crashed while holding the mutex - we now own it.
146+
acquired = true;
147+
}
148+
149+
try
150+
{
151+
GetInstalledWorkloadsCore(workloadResolver, installedWorkloads, sdkFeatureBand);
152+
return;
153+
}
154+
finally
155+
{
156+
if (acquired)
157+
{
158+
mutex.ReleaseMutex();
159+
}
160+
}
161+
}
162+
catch (Exception) when (attempt < MaxRetryAttempts)
163+
{
164+
// Retry with exponential backoff for transient COM failures.
165+
Thread.Sleep(RetryBaseDelayMs * (1 << attempt));
166+
}
167+
}
168+
}
169+
170+
private static unsafe void GetInstalledWorkloadsCore(
171+
IWorkloadResolver workloadResolver,
172+
InstalledWorkloadsCollection installedWorkloads,
173+
SdkFeatureBand? sdkFeatureBand)
108174
{
109175
if (!ComClassFactory.TryCreate(CLSID.SetupConfiguration, out ComClassFactory? factory, out HRESULT result))
110176
{

0 commit comments

Comments
 (0)