-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Brief description of your issue
There is a threading issue with C# (maybe C++ but I did not test this) COM commands on machines with 1 CPU Core. If executing a function like InstallPackageAsync, the program will permanently hang until manual user termination. In my experience, it appears to get stuck waiting for an event within the code below: (This is inside of PackageManager.cpp within the Microsoft.Management.Deployment project)
while (!completionEventFired)
{
DWORD dwEvent = WaitForMultipleObjects(
_countof(operationEvents) /* number of events */,
operationEvents /* event array */,
FALSE /* bWaitAll, FALSE to wake on any event */,
INFINITE /* wait until operation completion */);
switch (dwEvent)
{
// operationEvents[0] was signaled, progress
case WAIT_OBJECT_0 + 0:
// The report_progress call will hang when making callbacks to suspended processes so it's important that this is now on a background thread.
// Progress events are not queued - some will be missed if multiple progress events are fired from the ComContext to the callback
// while the report_progress call is hung\in progress.
// Duplicate progress events can be fired if another progress event comes from the ComContext to the callback after the listener
// has been awaked, but before it has gotten the installProgress.
report_progress(operationProgress);
break;
// operationEvents[1] was signaled, operation completed
case WAIT_OBJECT_0 + 1:
completionEventFired = true;
break;
// Return value is invalid.
default:
THROW_LAST_ERROR();
}
}
I believe the culprit is in the threading logic specified inside of the constructor of ContextOrchestrator
ContextOrchestrator::ContextOrchestrator()
{
ProgressCallback progress;
m_installingWriteableSource = Repository::Source(Repository::PredefinedSource::Installing);
m_installingWriteableSource.Open(progress);
// Decide how many threads to use for each command.
// We always allow only one install at a time.
// For download, if we can find the number of supported concurrent threads,
// use that as the maximum (up to 3); otherwise use a single thread.
const auto supportedConcurrentThreads = std::thread::hardware_concurrency();
const UINT32 maxDownloadThreads = 3;
const UINT32 operationThreads = 1;
const UINT32 downloadThreads = std::min(supportedConcurrentThreads ? supportedConcurrentThreads - 1 : 1, maxDownloadThreads);
AddCommandQueue(COMDownloadCommand::CommandName, downloadThreads);
AddCommandQueue(OperationCommandQueueName, operationThreads);
}
On the Azure VM box I was testing on, which is a DS1 v2 (1vCPU and 3.5gb RAM), I was indefinitely hanging. However, on a different box with >1 vCPU's I was not getting the issue.
When debugging into the constructor, I noticed that supportedConcurrentThreads would return 1 (due to 1 vCPU) and consequently, downloadThreads would be equal to 0 (therefore causing an indefinite hang).
I think the case of supportedConcurrentThreads = 0 is handled (defaults to 1), but I did not test it so I cannot confirm with 100% certainty that this is the case.
To test a solution, I just changed the code to a hacky solution like so:
const auto supportedConcurrentThreads = std::thread::hardware_concurrency();
const UINT32 maxDownloadThreads = 3;
const UINT32 operationThreads = 1;
UINT32 downloadThreads = std::min(supportedConcurrentThreads ? supportedConcurrentThreads - 1 : 1, maxDownloadThreads);
if(downloadThreads == 0)
downloadThreads = 3;
Note: I think ContextOrchestrator is just used for COM and as such I noted this as a COM issue. I tried to debug winget.exe and did not hit the code at all which is what led me to believe this is a COM issue.
Additional Note: I was able to do an UninstallPackageAsync without anything indefinite hang issues (reinforces my belief that downloadThreads = 0 is the culprit).
Steps to reproduce
Use an VM with only 1 vCPU and use C# InProc COM APIs (specifically something like InstallPackageAsync or DownloadPackageAsync).
Expected behavior
Download/Install COM behavior should execute.
Actual behavior
Download/Install COM behavior perpetually hangs.
Environment
Windows Package Manager v1.10.340
Copyright (c) Microsoft Corporation. All rights reserved.
Windows: Windows.Desktop v10.0.26100.3476
System Architecture: X64
Package: Microsoft.DesktopAppInstaller v1.25.340.0
Winget Directories
-----------------------------------------------------------------------------------------------------------------------
Logs %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\Diag…
User Settings %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\sett…
Portable Links Directory (User) %LOCALAPPDATA%\Microsoft\WinGet\Links
Portable Links Directory (Machine) C:\Program Files\WinGet\Links
Portable Package Root (User) %LOCALAPPDATA%\Microsoft\WinGet\Packages
Portable Package Root C:\Program Files\WinGet\Packages
Portable Package Root (x86) C:\Program Files (x86)\WinGet\Packages
Installer Downloads %USERPROFILE%\Downloads
Configuration Modules %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules
Links
---------------------------------------------------------------------------
Privacy Statement https://aka.ms/winget-privacy
License Agreement https://aka.ms/winget-license
Third Party Notices https://aka.ms/winget-3rdPartyNotice
Homepage https://aka.ms/winget
Windows Store Terms https://www.microsoft.com/en-us/storedocs/terms-of-sale
Admin Setting State
--------------------------------------------------
LocalManifestFiles Disabled
BypassCertificatePinningForMicrosoftStore Disabled
InstallerHashOverride Disabled
LocalArchiveMalwareScanOverride Disabled
ProxyCommandLineOptions Disabled
DefaultProxy Disabled