winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 1cc043b541fff91f58065b8e015aeedde356aa01
parent 5d6c80583549c447e6068c5f96d7f6794a91fed9
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Thu, 18 May 2023 19:19:24 -0700

Configuration cancellation support (#3244)

Adds cancellation support to the configuration `Async` functions, as well as implementing most of the non-async functions without using an extra thread.  The goal is that all cancellations will result in the same `HRESULT` being returned, regardless of which code is actually detecting the cancellation and ending early.

This change also updates the `winget` configuration workflows to pass along cancellation requests and better respond when things are cancelled.

Finally, using the previously implemented progress message system, cancellation will generically inject a message to the user so that it is clear that a `CTRL+C` signal has been received.
Diffstat:
Msrc/AppInstallerCLICore/ExecutionProgress.cpp | 27++++++++++++++++++++++-----
Msrc/AppInstallerCLICore/ExecutionProgress.h | 7++++---
Msrc/AppInstallerCLICore/ExecutionReporter.cpp | 5+++--
Msrc/AppInstallerCLICore/Resources.h | 1+
Msrc/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp | 166++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 3+++
Msrc/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj | 1+
Msrc/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters | 3+++
Asrc/AppInstallerSharedLib/Public/winget/AsyncTokens.h | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Microsoft.Management.Configuration/ConfigurationProcessor.cpp | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/Microsoft.Management.Configuration/ConfigurationProcessor.h | 19+++++++++++++++++++
Msrc/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h | 12++++++++++--
Msrc/Microsoft.Management.Configuration/ConfigurationSetParser.cpp | 34----------------------------------
Msrc/Microsoft.Management.Configuration/ConfigurationSetParser.h | 3---
Msrc/Microsoft.Management.Configuration/Telemetry/Telemetry.cpp | 17+++++++++++++++++
Msrc/Microsoft.Management.Configuration/Telemetry/Telemetry.h | 6++++++
17 files changed, 604 insertions(+), 175 deletions(-)

diff --git a/src/AppInstallerCLICore/ExecutionProgress.cpp b/src/AppInstallerCLICore/ExecutionProgress.cpp @@ -164,12 +164,12 @@ namespace AppInstaller::CLI::Execution } } - void ProgressVisualizerBase::SetMessage(std::string_view message) + void ProgressVisualizerBase::Message(std::string_view message) { - std::atomic_store(&m_message, std::make_shared<std::string>(message)); + std::atomic_store(&m_message, std::make_shared<Utility::NormalizedString>(message)); } - std::shared_ptr<std::string> ProgressVisualizerBase::GetMessage() + std::shared_ptr<Utility::NormalizedString> ProgressVisualizerBase::Message() { return std::atomic_load(&m_message); } @@ -211,6 +211,8 @@ namespace AppInstaller::CLI::Execution // Indent two spaces for the spinner, but three here so that we can overwrite it in the loop. std::string_view indent = " "; + std::shared_ptr<Utility::NormalizedString> message = this->Message(); + size_t messageLength = message ? Utility::UTF8ColumnWidth(*message) : 0; for (size_t i = 0; !m_canceled; ++i) { @@ -218,8 +220,23 @@ namespace AppInstaller::CLI::Execution ApplyStyle(i % repetitionCount, repetitionCount, true); m_out << '\r' << indent << spinnerChars[i % ARRAYSIZE(spinnerChars)]; m_out.RestoreDefault(); - std::shared_ptr<std::string> message = this->GetMessage(); - m_out << ' ' << (message ? *message : std::string{}) << std::flush; + + std::shared_ptr<Utility::NormalizedString> newMessage = this->Message(); + std::string eraser; + if (newMessage) + { + size_t newLength = Utility::UTF8ColumnWidth(*newMessage); + + if (newLength < messageLength) + { + eraser = std::string(messageLength - newLength, ' '); + } + + message = newMessage; + messageLength = newLength; + } + + m_out << ' ' << (message ? *message : std::string{}) << eraser << std::flush; Sleep(250); } diff --git a/src/AppInstallerCLICore/ExecutionProgress.h b/src/AppInstallerCLICore/ExecutionProgress.h @@ -3,6 +3,7 @@ #pragma once #include "VTSupport.h" #include <AppInstallerProgress.h> +#include <AppInstallerStrings.h> #include <winget/UserSettings.h> #include <ChannelStreams.h> @@ -28,8 +29,8 @@ namespace AppInstaller::CLI::Execution void SetStyle(AppInstaller::Settings::VisualStyle style) { m_style = style; } - void SetMessage(std::string_view message); - std::shared_ptr<std::string> GetMessage(); + void Message(std::string_view message); + std::shared_ptr<Utility::NormalizedString> Message(); protected: BaseStream& m_out; @@ -44,7 +45,7 @@ namespace AppInstaller::CLI::Execution private: bool m_enableVT = false; - std::shared_ptr<std::string> m_message; + std::shared_ptr<Utility::NormalizedString> m_message; }; } diff --git a/src/AppInstallerCLICore/ExecutionReporter.cpp b/src/AppInstallerCLICore/ExecutionReporter.cpp @@ -221,12 +221,12 @@ namespace AppInstaller::CLI::Execution { if (m_spinner) { - m_spinner->SetMessage(message); + m_spinner->Message(message); } if (m_progressBar) { - m_progressBar->SetMessage(message); + m_progressBar->Message(message); } } @@ -302,6 +302,7 @@ namespace AppInstaller::CLI::Execution ProgressCallback* callback = m_progressCallback.load(); if (callback) { + callback->SetProgressMessage(Resource::String::CancellingOperation()); callback->Cancel(); } } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h @@ -40,6 +40,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(AvailableUpgrades); WINGET_DEFINE_RESOURCE_STRINGID(BothManifestAndSearchQueryProvided); WINGET_DEFINE_RESOURCE_STRINGID(Cancelled); + WINGET_DEFINE_RESOURCE_STRINGID(CancellingOperation); WINGET_DEFINE_RESOURCE_STRINGID(ChannelArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(Command); WINGET_DEFINE_RESOURCE_STRINGID(CommandArgumentDescription); diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -418,10 +418,68 @@ namespace AppInstaller::CLI::Workflow return Resource::String::ConfigurationUnitSkipped(resultCode); } + // Coordinates an active progress scope and cancellation of the operation. + template<typename OperationT> + struct ProgressCancellationUnification + { + ProgressCancellationUnification(std::unique_ptr<Reporter::AsyncProgressScope>&& progressScope, const OperationT& operation) : + m_progressScope(std::move(progressScope)), m_operation(operation) + { + SetCancellationFunction(); + } + + void Reset() + { + m_cancelScope.reset(); + m_progressScope.reset(); + } + + Reporter::AsyncProgressScope& Progress() const { return *m_progressScope; } + + void Progress(std::unique_ptr<Reporter::AsyncProgressScope>&& progressScope) + { + m_cancelScope.reset(); + m_progressScope = std::move(progressScope); + SetCancellationFunction(); + } + + OperationT& Operation() const { return m_operation; } + + private: + void SetCancellationFunction() + { + if (m_progressScope) + { + m_cancelScope = m_progressScope->Callback().SetCancellationFunction([this]() { m_operation.Cancel(); }); + } + } + + std::unique_ptr<Reporter::AsyncProgressScope> m_progressScope; + OperationT m_operation; + IProgressCallback::CancelFunctionRemoval m_cancelScope; + }; + + template<typename Operation> + ProgressCancellationUnification<Operation> CreateProgressCancellationUnification( + std::unique_ptr<Reporter::AsyncProgressScope>&& progressScope, + const Operation& operation) + { + return { std::move(progressScope), operation }; + } + // Helper to handle progress callbacks from ApplyConfigurationSetAsync struct ApplyConfigurationSetProgressOutput { - ApplyConfigurationSetProgressOutput(Context& context) : m_context(context) {} + using ApplyOperation = IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData>; + + ApplyConfigurationSetProgressOutput(Context& context, const ApplyOperation& operation) : + m_context(context), m_unification({}, operation) + { + operation.Progress([&](const IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData>& operation, const ConfigurationSetChangeData& data) + { + Progress(operation, data); + }); + } void Progress(const IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData>& operation, const ConfigurationSetChangeData& data) { @@ -440,13 +498,13 @@ namespace AppInstaller::CLI::Workflow { case ConfigurationSetState::Pending: m_context.Reporter.Info() << Resource::String::ConfigurationWaitingOnAnother << std::endl; - m_context.Reporter.BeginProgress(); + BeginProgress(); break; case ConfigurationSetState::InProgress: - m_context.Reporter.EndProgress(true); + EndProgress(); break; case ConfigurationSetState::Completed: - m_context.Reporter.EndProgress(true); + EndProgress(); break; } } @@ -486,11 +544,11 @@ namespace AppInstaller::CLI::Workflow break; case ConfigurationUnitState::InProgress: OutputUnitInProgressIfNeeded(unit); - m_context.Reporter.BeginProgress(); + BeginProgress(); break; case ConfigurationUnitState::Completed: OutputUnitInProgressIfNeeded(unit); - m_context.Reporter.EndProgress(true); + EndProgress(); if (SUCCEEDED(resultInformation.ResultCode())) { m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationSuccessfullyApplied << std::endl; @@ -570,7 +628,18 @@ namespace AppInstaller::CLI::Workflow // 2. 1/N VT progress reporting for configuration units while also showing a spinner for the unit itself } + void BeginProgress() + { + m_unification.Progress(m_context.Reporter.BeginAsyncProgress(true)); + } + + void EndProgress() + { + m_unification.Reset(); + } + Context& m_context; + ProgressCancellationUnification<ApplyOperation> m_unification; std::set<winrt::guid> m_unitsSeen; std::set<winrt::guid> m_unitsCompleted; bool m_isFirstProgress = true; @@ -618,9 +687,19 @@ namespace AppInstaller::CLI::Workflow std::filesystem::path absolutePath = GetConfigurationFilePath(context); Streams::IInputStream inputStream = nullptr; - inputStream = Streams::FileRandomAccessStream::OpenAsync(absolutePath.wstring(), FileAccessMode::Read).get(); + { + auto openAction = Streams::FileRandomAccessStream::OpenAsync(absolutePath.wstring(), FileAccessMode::Read); + auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { openAction.Cancel(); }); + inputStream = openAction.get(); + } + + OpenConfigurationSetResult openResult = nullptr; + { + auto openAction = context.Get<Data::ConfigurationContext>().Processor().OpenConfigurationSetAsync(inputStream); + auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { openAction.Cancel(); }); + openResult = openAction.get(); + } - OpenConfigurationSetResult openResult = context.Get<Data::ConfigurationContext>().Processor().OpenConfigurationSet(inputStream); if (FAILED_LOG(static_cast<HRESULT>(openResult.ResultCode().value))) { AICLI_LOG(Config, Error, << "Failed to open configuration set at " << absolutePath.u8string() << " with error 0x" << Logging::SetHRFormat << static_cast<HRESULT>(openResult.ResultCode().value)); @@ -681,6 +760,7 @@ namespace AppInstaller::CLI::Workflow progressScope->Callback().SetProgressMessage(gettingDetailString); auto getDetailsOperation = configContext.Processor().GetSetDetailsAsync(configContext.Set(), ConfigurationUnitDetailLevel::Catalog); + auto unification = CreateProgressCancellationUnification(std::move(progressScope), getDetailsOperation); OutputStream out = context.Reporter.Info(); uint32_t unitsShown = 0; @@ -689,7 +769,7 @@ namespace AppInstaller::CLI::Workflow { auto threadContext = context.SetForCurrentThread(); - progressScope.reset(); + unification.Reset(); auto unitResults = operation.GetResults().UnitResults(); for (unitsShown; unitsShown < unitResults.Size(); ++unitsShown) @@ -701,38 +781,58 @@ namespace AppInstaller::CLI::Workflow progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(gettingDetailString); + unification.Progress(std::move(progressScope)); }); + HRESULT hr = S_OK; + GetConfigurationSetDetailsResult result = nullptr; + try { - GetConfigurationSetDetailsResult result = getDetailsOperation.get(); + result = getDetailsOperation.get(); + } + catch (...) + { + hr = LOG_CAUGHT_EXCEPTION(); + } - progressScope.reset(); + unification.Reset(); - // Handle any missing progress callbacks - auto unitResults = result.UnitResults(); - for (unitsShown; unitsShown < unitResults.Size(); ++unitsShown) - { - GetConfigurationUnitDetailsResult unitResult = unitResults.GetAt(unitsShown); - LogFailedGetConfigurationUnitDetails(unitResult.Unit(), unitResult.ResultInformation()); - OutputConfigurationUnitInformation(out, unitResult.Unit()); - } + if (context.IsTerminated()) + { + // The context should only be terminated on us due to cancellation + context.Reporter.Error() << Resource::String::Cancelled << std::endl; + return; } - CATCH_LOG(); - progressScope.reset(); - - // In the event of an exception from GetSetDetailsAsync, show the data we do have - if (!unitsShown) + if (FAILED(hr)) { // Failing to get details might not be fatal, warn about it but proceed context.Reporter.Warn() << Resource::String::ConfigurationFailedToGetDetails << std::endl; + } - for (const ConfigurationUnit& unit : configContext.Set().ConfigurationUnits()) + // Handle any missing progress callbacks that are in the results + if (result) + { + auto unitResults = result.UnitResults(); + if (unitResults) { - OutputConfigurationUnitInformation(out, unit); + for (unitsShown; unitsShown < unitResults.Size(); ++unitsShown) + { + GetConfigurationUnitDetailsResult unitResult = unitResults.GetAt(unitsShown); + LogFailedGetConfigurationUnitDetails(unitResult.Unit(), unitResult.ResultInformation()); + OutputConfigurationUnitInformation(out, unitResult.Unit()); + } } } + + // Handle any units that are NOT in the results (due to an exception part of the way through) + auto allUnits = configContext.Set().ConfigurationUnits(); + for (unitsShown; unitsShown < allUnits.Size(); ++unitsShown) + { + ConfigurationUnit unit = allUnits.GetAt(unitsShown); + OutputConfigurationUnitInformation(out, unit); + } } void ShowConfigurationSetConflicts(Execution::Context& context) @@ -761,24 +861,12 @@ namespace AppInstaller::CLI::Workflow void ApplyConfigurationSet(Execution::Context& context) { - ApplyConfigurationSetProgressOutput progress{ context }; ApplyConfigurationSetResult result = nullptr; - ConfigurationContext& configContext = context.Get<Data::ConfigurationContext>(); { - // Just in case, forcibly stop our manual progress - auto hideProgress = wil::scope_exit([&]() - { - context.Reporter.EndProgress(true); - }); - auto applyOperation = configContext.Processor().ApplySetAsync(configContext.Set(), ApplyConfigurationSetFlags::None); - - applyOperation.Progress([&](const IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData>& operation, const ConfigurationSetChangeData& data) - { - progress.Progress(operation, data); - }); + ApplyConfigurationSetProgressOutput progress{ context, applyOperation }; result = applyOperation.get(); progress.HandleUnreportedProgress(result); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1936,4 +1936,7 @@ Please specify one of them using the --source option to proceed.</value> <value>See line {0}, column {1} in the file.</value> <comment>{Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively.</comment> </data> + <data name="CancellingOperation" xml:space="preserve"> + <value>Cancelling operation</value> + </data> </root> \ No newline at end of file diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj @@ -398,6 +398,7 @@ <ClInclude Include="Public\AppInstallerVersions.h" /> <ClInclude Include="Public\Telemetry\MicrosoftTelemetry.h" /> <ClInclude Include="Public\Telemetry\WinEventLogLevels.h" /> + <ClInclude Include="Public\winget\AsyncTokens.h" /> <ClInclude Include="Public\winget\JsonSchemaValidation.h" /> <ClInclude Include="Public\winget\LocIndependent.h" /> <ClInclude Include="Public\winget\Resources.h" /> diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters @@ -71,6 +71,9 @@ <ClInclude Include="Public\winget\Runtime.h"> <Filter>Public\winget</Filter> </ClInclude> + <ClInclude Include="Public\winget\AsyncTokens.h"> + <Filter>Public\winget</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> diff --git a/src/AppInstallerSharedLib/Public/winget/AsyncTokens.h b/src/AppInstallerSharedLib/Public/winget/AsyncTokens.h @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <AppInstallerLogging.h> +#include <winrt/Windows.Foundation.h> +#include <memory> + +namespace AppInstaller::WinRT +{ + namespace details + { + // Type erasing interface for winrt cancellation token. + struct AsyncCancellationTypeErasure + { + virtual ~AsyncCancellationTypeErasure() = default; + + virtual bool IsCancelled() const noexcept = 0; + virtual void Callback(winrt::delegate<>&& callback) const noexcept = 0; + }; + + // Type containing winrt cancellation token wrapper. + template <typename Promise> + struct AsyncCancellationT : public AsyncCancellationTypeErasure + { + using Token = winrt::impl::cancellation_token<Promise>; + + AsyncCancellationT(Token&& token) : m_token(std::move(token)) {} + + bool IsCancelled() const noexcept override + { + return m_token(); + } + + void Callback(winrt::delegate<>&& callback) const noexcept override + { + m_token.callback(std::move(callback)); + } + + private: + Token m_token; + }; + } + + // May hold a cancellation token and provide the ability to check its status. + // If empty, it will act as if it is never cancelled. + struct AsyncCancellation + { + // Create an empty cancellation object, which will never be cancelled. + AsyncCancellation() = default; + + // Create a cancellation object from the winrt token. + template <typename Promise> + AsyncCancellation(winrt::impl::cancellation_token<Promise>&& token) + { + m_token = std::make_unique<details::AsyncCancellationT<Promise>>(std::move(token)); + } + + // Returns true if the operation has been cancelled, false if not. + bool IsCancelled() const noexcept + { + return m_token ? m_token->IsCancelled() : false; + } + + // Throws the appropriate exception if the operation has been cancelled. + void ThrowIfCancelled() const + { + if (IsCancelled()) + { + AICLI_LOG(Core, Warning, << "Operation cancelled"); + throw winrt::hresult_canceled(); + } + } + + // Sets a callback that will be invoked on cancellation. + void Callback(winrt::delegate<>&& callback) const noexcept + { + if (m_token) + { + m_token->Callback(std::move(callback)); + } + } + + private: + std::unique_ptr<details::AsyncCancellationTypeErasure> m_token; + }; + + namespace details + { + // Type erasing interface for winrt progress token. + template <typename ResultT, typename ProgressT> + struct AsyncProgressTypeErasure + { + virtual ~AsyncProgressTypeErasure() = default; + + virtual void Progress(ProgressT const& progress) const = 0; + + virtual void Result(ResultT const& result) const = 0; + }; + + // Type containing winrt progress token wrapper. + template <typename Promise, typename ResultT, typename ProgressT> + struct AsyncProgressT : public AsyncProgressTypeErasure<ResultT, ProgressT> + { + using Token = winrt::impl::progress_token<Promise, ProgressT>; + + AsyncProgressT(Token&& token) : m_token(std::move(token)) {} + + void Progress(ProgressT const& progress) const override + { + m_token(progress); + } + + void Result(ResultT const& result) const override + { + m_token.set_result(result); + } + + private: + Token m_token; + }; + } + + // May hold a progress token and provide the ability to send progress updates. + // If empty, progress will be dropped on calls here. + template <typename ResultT, typename ProgressT> + struct AsyncProgress : public AsyncCancellation + { + // Create an empty progress object. + AsyncProgress() = default; + + // Create a progress object from the winrt token. + template <typename Promise> + AsyncProgress(winrt::impl::progress_token<Promise, ProgressT>&& progress, winrt::impl::cancellation_token<Promise>&& cancellation) : + AsyncCancellation(std::move(cancellation)) + { + m_token = std::make_unique<details::AsyncProgressT<Promise, ResultT, ProgressT>>(std::move(progress)); + } + + // Sends progress if this object is not empty. + void Progress(ProgressT const& progress) const + { + if (m_token) + { + m_token->Progress(progress); + } + } + + // Sets the result onto the progress object if it is not empty. + void Result(ResultT const& result) const + { + if (m_token) + { + m_token->Result(result); + } + } + + private: + std::unique_ptr<details::AsyncProgressTypeErasure<ResultT, ProgressT>> m_token; + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp b/src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp @@ -208,11 +208,13 @@ namespace winrt::Microsoft::Management::Configuration::implementation { Windows::Storage::Streams::IInputStream localStream = stream; co_await winrt::resume_background(); + auto cancellation = co_await get_cancellation_token(); + cancellation.enable_propagation(); auto threadGlobals = m_threadGlobals.SetForCurrentThread(); auto result = make_self<wil::details::module_count_wrapper<OpenConfigurationSetResult>>(); - if (!stream) + if (!localStream) { result->Initialize(E_POINTER, {}); co_return *result; @@ -220,7 +222,32 @@ namespace winrt::Microsoft::Management::Configuration::implementation try { - std::unique_ptr<ConfigurationSetParser> parser = ConfigurationSetParser::Create(localStream); + // Read the entire file into memory as we expect them to be small and + // our YAML parser doesn't support streaming at this time. + // This is done here to enable easy cancellation propagation to the stream reads. + uint32_t bufferSize = 1 << 20; + Windows::Storage::Streams::Buffer buffer(bufferSize); + Windows::Storage::Streams::InputStreamOptions readOptions = + Windows::Storage::Streams::InputStreamOptions::Partial | Windows::Storage::Streams::InputStreamOptions::ReadAhead; + std::string inputString; + + for (;;) + { + Windows::Storage::Streams::IBuffer readBuffer = co_await localStream.ReadAsync(buffer, bufferSize, readOptions); + + size_t readSize = static_cast<size_t>(readBuffer.Length()); + if (readSize) + { + static_assert(sizeof(char) == sizeof(*readBuffer.data())); + inputString.append(reinterpret_cast<char*>(readBuffer.data()), readSize); + } + else + { + break; + } + } + + std::unique_ptr<ConfigurationSetParser> parser = ConfigurationSetParser::Create(inputString); if (FAILED(parser->Result())) { result->Initialize(parser->Result(), parser->Field(), parser->Value(), parser->Line(), parser->Column()); @@ -269,7 +296,8 @@ namespace winrt::Microsoft::Management::Configuration::implementation Configuration::GetConfigurationSetDetailsResult ConfigurationProcessor::GetSetDetails(const ConfigurationSet& configurationSet, ConfigurationUnitDetailLevel detailLevel) { - return GetSetDetailsAsync(configurationSet, detailLevel).get(); + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + return GetSetDetailsImpl(configurationSet, detailLevel); } Windows::Foundation::IAsyncOperationWithProgress<Configuration::GetConfigurationSetDetailsResult, Configuration::GetConfigurationUnitDetailsResult> ConfigurationProcessor::GetSetDetailsAsync(const ConfigurationSet& configurationSet, ConfigurationUnitDetailLevel detailLevel) @@ -279,16 +307,25 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationSet localSet = configurationSet; co_await winrt::resume_background(); + co_return GetSetDetailsImpl(localSet, detailLevel, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token()}); + } + + Configuration::GetConfigurationSetDetailsResult ConfigurationProcessor::GetSetDetailsImpl( + const ConfigurationSet& configurationSet, + ConfigurationUnitDetailLevel detailLevel, + AppInstaller::WinRT::AsyncProgress<GetConfigurationSetDetailsResult, GetConfigurationUnitDetailsResult> progress) + { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); - IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(localSet); + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet); - auto progress = co_await winrt::get_progress_token(); auto result = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationSetDetailsResult>>(); - progress.set_result(*result); + progress.Result(*result); - for (const auto& unit : localSet.ConfigurationUnits()) + for (const auto& unit : configurationSet.ConfigurationUnits()) { + progress.ThrowIfCancelled(); + auto unitResult = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationUnitDetailsResult>>(); auto unitResultInformation = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); unitResult->Unit(unit); @@ -305,15 +342,16 @@ namespace winrt::Microsoft::Management::Configuration::implementation } result->UnitResultsVector().Append(*unitResult); - progress(*unitResult); + progress.Progress(*unitResult); } - co_return *result; + return *result; } void ConfigurationProcessor::GetUnitDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel) { - return GetUnitDetailsAsync(unit, detailLevel).get(); + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + return GetUnitDetailsImpl(unit, detailLevel); } Windows::Foundation::IAsyncAction ConfigurationProcessor::GetUnitDetailsAsync(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel) @@ -323,16 +361,22 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); + co_return GetUnitDetailsImpl(localUnit, detailLevel); + } + + void ConfigurationProcessor::GetUnitDetailsImpl(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel) + { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); - IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(localUnit, detailLevel); - get_self<implementation::ConfigurationUnit>(localUnit)->Details(std::move(details)); + IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(unit, detailLevel); + get_self<implementation::ConfigurationUnit>(unit)->Details(std::move(details)); } Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySet(const ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags) { - return ApplySetAsync(configurationSet, flags).get(); + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + return ApplySetImpl(configurationSet, flags); } Windows::Foundation::IAsyncOperationWithProgress<Configuration::ApplyConfigurationSetResult, Configuration::ConfigurationSetChangeData> ConfigurationProcessor::ApplySetAsync(const ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags) @@ -341,25 +385,30 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationSet localSet = configurationSet; co_await winrt::resume_background(); - auto progress = co_await winrt::get_progress_token(); + co_return ApplySetImpl(localSet, flags, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token() }); + } + + Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySetImpl( + const ConfigurationSet& configurationSet, + ApplyConfigurationSetFlags flags, + AppInstaller::WinRT::AsyncProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> progress) + { // TODO: Not needed until we have history implemented UNREFERENCED_PARAMETER(flags); auto threadGlobals = m_threadGlobals.SetForCurrentThread(); - auto result = make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationSetResult>>(); - ConfigurationSetApplyProcessor applyProcessor{ localSet, m_threadGlobals.GetTelemetryLogger(), m_factory.CreateSetProcessor(localSet), result, progress}; - progress.set_result(*result); - + ConfigurationSetApplyProcessor applyProcessor{ configurationSet, m_threadGlobals.GetTelemetryLogger(), m_factory.CreateSetProcessor(configurationSet), std::move(progress) }; applyProcessor.Process(); - co_return *result; + return applyProcessor.Result(); } Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSet(const ConfigurationSet& configurationSet) { - return TestSetAsync(configurationSet).get(); + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + return TestSetImpl(configurationSet); } Windows::Foundation::IAsyncOperationWithProgress<Configuration::TestConfigurationSetResult, Configuration::TestConfigurationUnitResult> ConfigurationProcessor::TestSetAsync(const ConfigurationSet& configurationSet) @@ -368,75 +417,102 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationSet localSet = configurationSet; co_await winrt::resume_background(); - auto progress = co_await winrt::get_progress_token(); + co_return TestSetImpl(localSet, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token() }); + } + + Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSetImpl( + const ConfigurationSet& configurationSet, + AppInstaller::WinRT::AsyncProgress<TestConfigurationSetResult, TestConfigurationUnitResult> progress) + { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); - IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(localSet); + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet); auto result = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationSetResult>>(); result->TestResult(ConfigurationTestResult::NotRun); - progress.set_result(*result); + progress.Result(*result); - for (const auto& unit : localSet.ConfigurationUnits()) + try { - AICLI_LOG(Config, Info, << "Testing configuration unit: " << AppInstaller::Utility::ConvertToUTF8(unit.UnitName())); - - auto testResult = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationUnitResult>>(); - auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); - testResult->Initialize(unit, *unitResult); - - if (ShouldTestDuringTest(unit.Intent())) + for (const auto& unit : configurationSet.ConfigurationUnits()) { - IConfigurationUnitProcessor unitProcessor; + AICLI_LOG(Config, Info, << "Testing configuration unit: " << AppInstaller::Utility::ConvertToUTF8(unit.UnitName())); - try - { - // TODO: Directives overlay to prevent running elevated for test - unitProcessor = setProcessor.CreateUnitProcessor(unit, {}); - } - catch (...) - { - ExtractUnitResultInformation(std::current_exception(), unitResult); - } + auto testResult = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationUnitResult>>(); + auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); + testResult->Initialize(unit, *unitResult); - if (unitProcessor) + if (ShouldTestDuringTest(unit.Intent())) { + progress.ThrowIfCancelled(); + + IConfigurationUnitProcessor unitProcessor; + try { - TestSettingsResult settingsResult = unitProcessor.TestSettings(); - testResult->TestResult(settingsResult.TestResult()); - testResult->ResultInformation(settingsResult.ResultInformation()); + // TODO: Directives overlay to prevent running elevated for test + unitProcessor = setProcessor.CreateUnitProcessor(unit, {}); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } - m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(localSet.InstanceIdentifier(), unit, ConfigurationUnitIntent::Assert, TelemetryTraceLogger::TestAction, testResult->ResultInformation()); + progress.ThrowIfCancelled(); + + if (unitProcessor) + { + try + { + TestSettingsResult settingsResult = unitProcessor.TestSettings(); + testResult->TestResult(settingsResult.TestResult()); + testResult->ResultInformation(settingsResult.ResultInformation()); + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitResult); + } + + m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate( + configurationSet.InstanceIdentifier(), + unit, + ConfigurationUnitIntent::Assert, + TelemetryTraceLogger::TestAction, + testResult->ResultInformation()); + } + } + else + { + testResult->TestResult(ConfigurationTestResult::NotRun); } - } - else - { - testResult->TestResult(ConfigurationTestResult::NotRun); - } - if (FAILED(unitResult->ResultCode())) - { - testResult->TestResult(ConfigurationTestResult::Failed); - } + if (FAILED(unitResult->ResultCode())) + { + testResult->TestResult(ConfigurationTestResult::Failed); + } - result->AppendUnitResult(*testResult); + result->AppendUnitResult(*testResult); - progress(*testResult); - } + progress.Progress(*testResult); + } - m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTest(*winrt::get_self<implementation::ConfigurationSet>(localSet), *result); - co_return *result; + m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTest(*winrt::get_self<implementation::ConfigurationSet>(configurationSet), *result); + return *result; + } + catch (...) + { + m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTestException( + *winrt::get_self<implementation::ConfigurationSet>(configurationSet), + LOG_CAUGHT_EXCEPTION(), + *result); + throw; + } } Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettings(const ConfigurationUnit& unit) { - return GetUnitSettingsAsync(unit).get(); + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + return GetUnitSettingsImpl(unit); } Windows::Foundation::IAsyncOperation<Configuration::GetConfigurationUnitSettingsResult> ConfigurationProcessor::GetUnitSettingsAsync(const ConfigurationUnit& unit) @@ -446,6 +522,13 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); + co_return GetUnitSettingsImpl(localUnit, { co_await winrt::get_cancellation_token() }); + } + + Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettingsImpl( + const ConfigurationUnit& unit, + AppInstaller::WinRT::AsyncCancellation cancellation) + { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); @@ -453,18 +536,22 @@ namespace winrt::Microsoft::Management::Configuration::implementation auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); result->ResultInformation(*unitResult); + cancellation.ThrowIfCancelled(); + IConfigurationUnitProcessor unitProcessor; try { // TODO: Directives overlay to prevent running elevated for get - unitProcessor = setProcessor.CreateUnitProcessor(localUnit, {}); + unitProcessor = setProcessor.CreateUnitProcessor(unit, {}); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } + cancellation.ThrowIfCancelled(); + if (unitProcessor) { try @@ -478,10 +565,10 @@ namespace winrt::Microsoft::Management::Configuration::implementation ExtractUnitResultInformation(std::current_exception(), unitResult); } - m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, localUnit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::GetAction, result->ResultInformation()); + m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::GetAction, result->ResultInformation()); } - co_return *result; + return *result; } void ConfigurationProcessor::Diagnostics(DiagnosticLevel level, std::string_view message) diff --git a/src/Microsoft.Management.Configuration/ConfigurationProcessor.h b/src/Microsoft.Management.Configuration/ConfigurationProcessor.h @@ -6,6 +6,7 @@ #include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Storage.Streams.h> #include "ConfigThreadGlobals.h" +#include <winget/AsyncTokens.h> #include <string_view> #include <functional> @@ -78,6 +79,24 @@ namespace winrt::Microsoft::Management::Configuration::implementation void Diagnostics(DiagnosticLevel level, std::string_view message); private: + GetConfigurationSetDetailsResult GetSetDetailsImpl( + const ConfigurationSet& configurationSet, + ConfigurationUnitDetailLevel detailLevel, + AppInstaller::WinRT::AsyncProgress<GetConfigurationSetDetailsResult, GetConfigurationUnitDetailsResult> progress = {}); + + void GetUnitDetailsImpl(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel); + + ApplyConfigurationSetResult ApplySetImpl( + const ConfigurationSet& configurationSet, + ApplyConfigurationSetFlags flags, + AppInstaller::WinRT::AsyncProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> progress = {}); + + TestConfigurationSetResult TestSetImpl( + const ConfigurationSet& configurationSet, + AppInstaller::WinRT::AsyncProgress<TestConfigurationSetResult, TestConfigurationUnitResult> progress = {}); + + GetConfigurationUnitSettingsResult GetUnitSettingsImpl(const ConfigurationUnit& unit, AppInstaller::WinRT::AsyncCancellation cancellation = {}); + IConfigurationSetProcessorFactory m_factory = nullptr; event<Windows::Foundation::EventHandler<DiagnosticInformation>> m_diagnostics; event<Windows::Foundation::TypedEventHandler<ConfigurationSet, ConfigurationChangeData>> m_configurationChange; diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp @@ -24,9 +24,12 @@ namespace winrt::Microsoft::Management::Configuration::implementation const Configuration::ConfigurationSet& configurationSet, const TelemetryTraceLogger& telemetry, IConfigurationSetProcessor&& setProcessor, - result_type result, - const std::function<void(ConfigurationSetChangeData)>& progress) : - m_configurationSet(configurationSet), m_setProcessor(std::move(setProcessor)), m_telemetry(telemetry), m_result(std::move(result)), m_progress(progress) + AppInstaller::WinRT::AsyncProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData>&& progress) : + m_configurationSet(configurationSet), + m_setProcessor(std::move(setProcessor)), + m_telemetry(telemetry), + m_result(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationSetResult>>()), + m_progress(std::move(progress)) { // Create a copy of the set of configuration units auto unitsView = configurationSet.ConfigurationUnits(); @@ -39,23 +42,47 @@ namespace winrt::Microsoft::Management::Configuration::implementation m_unitInfo.emplace_back(unit); m_result->UnitResultsVector().Append(*m_unitInfo.back().Result); } + + m_progress.Result(*m_result); } void ConfigurationSetApplyProcessor::Process() { - if (PreProcess()) + try { - // TODO: Send pending when blocked by another configuration run - //SendProgress(ConfigurationSetState::Pending); + if (PreProcess()) + { + // TODO: Send pending when blocked by another configuration run + //SendProgress(ConfigurationSetState::Pending); - SendProgress(ConfigurationSetState::InProgress); + SendProgress(ConfigurationSetState::InProgress); - ProcessInternal(HasProcessedSuccessfully, &ConfigurationSetApplyProcessor::ProcessUnit, true); - } + ProcessInternal(HasProcessedSuccessfully, &ConfigurationSetApplyProcessor::ProcessUnit, true); + } - SendProgress(ConfigurationSetState::Completed); + SendProgress(ConfigurationSetState::Completed); - m_telemetry.LogConfigProcessingSummaryForApply(*winrt::get_self<implementation::ConfigurationSet>(m_configurationSet), *m_result); + m_telemetry.LogConfigProcessingSummaryForApply(*winrt::get_self<implementation::ConfigurationSet>(m_configurationSet), *m_result); + } + catch (...) + { + const auto& configurationSet = *winrt::get_self<implementation::ConfigurationSet>(m_configurationSet); + m_telemetry.LogConfigProcessingSummary( + configurationSet.InstanceIdentifier(), + configurationSet.IsFromHistory(), + ConfigurationUnitIntent::Apply, + LOG_CAUGHT_EXCEPTION(), + ConfigurationUnitResultSource::Internal, + GetProcessingSummaryFor(ConfigurationUnitIntent::Assert), + GetProcessingSummaryFor(ConfigurationUnitIntent::Inform), + GetProcessingSummaryFor(ConfigurationUnitIntent::Apply)); + throw; + } + } + + Configuration::ApplyConfigurationSetResult ConfigurationSetApplyProcessor::Result() const + { + return *m_result; } ConfigurationSetApplyProcessor::UnitInfo::UnitInfo(const Configuration::ConfigurationUnit& unit) : @@ -325,6 +352,8 @@ namespace winrt::Microsoft::Management::Configuration::implementation bool ConfigurationSetApplyProcessor::ProcessUnit(UnitInfo& unitInfo) { + m_progress.ThrowIfCancelled(); + IConfigurationUnitProcessor unitProcessor; // Once we get this far, consider the unit processed even if we fail to create the actual processor. @@ -353,6 +382,9 @@ namespace winrt::Microsoft::Management::Configuration::implementation return false; } + // As the process of creating the unit processor could take a while, check for cancellation again + m_progress.ThrowIfCancelled(); + bool result = false; std::string_view action; @@ -412,6 +444,9 @@ namespace winrt::Microsoft::Management::Configuration::implementation } else if (testSettingsResult.TestResult() == ConfigurationTestResult::Negative) { + // Just in case testing took a while, check for cancellation before moving on to applying + m_progress.ThrowIfCancelled(); + action = TelemetryTraceLogger::ApplyAction; ApplySettingsResult applySettingsResult = unitProcessor.ApplySettings(); if (SUCCEEDED(applySettingsResult.ResultInformation().ResultCode())) @@ -451,28 +486,22 @@ namespace winrt::Microsoft::Management::Configuration::implementation void ConfigurationSetApplyProcessor::SendProgress(ConfigurationSetState state) { - if (m_progress) + try { - try - { - m_progress(implementation::ConfigurationSetChangeData::Create(state)); - } - CATCH_LOG(); + m_progress.Progress(implementation::ConfigurationSetChangeData::Create(state)); } + CATCH_LOG(); } void ConfigurationSetApplyProcessor::SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo) { unitInfo.Result->State(state); - if (m_progress) + try { - try - { - m_progress(implementation::ConfigurationSetChangeData::Create(state, *unitInfo.ResultInformation, unitInfo.Unit)); - } - CATCH_LOG(); + m_progress.Progress(implementation::ConfigurationSetChangeData::Create(state, *unitInfo.ResultInformation, unitInfo.Unit)); } + CATCH_LOG(); } void ConfigurationSetApplyProcessor::SendProgressIfNotComplete(ConfigurationUnitState state, const UnitInfo& unitInfo) @@ -482,4 +511,29 @@ namespace winrt::Microsoft::Management::Configuration::implementation SendProgress(state, unitInfo); } } + + TelemetryTraceLogger::ProcessingSummaryForIntent ConfigurationSetApplyProcessor::GetProcessingSummaryFor(ConfigurationUnitIntent intent) const + { + TelemetryTraceLogger::ProcessingSummaryForIntent result{ intent, 0, 0, 0 }; + + for (const auto& unitInfo : m_unitInfo) + { + if (unitInfo.Unit.Intent() == intent) + { + ++result.Count; + + if (unitInfo.Processed) + { + ++result.Run; + + if (FAILED(unitInfo.ResultInformation->ResultCode())) + { + ++result.Failed; + } + } + } + } + + return result; + } } diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h b/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h @@ -7,6 +7,7 @@ #include "ApplyConfigurationUnitResult.h" #include "ConfigurationUnitResultInformation.h" #include "Telemetry/Telemetry.h" +#include <winget/AsyncTokens.h> #include <map> #include <string> @@ -17,6 +18,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation // A helper to better organize the configuration set Apply. struct ConfigurationSetApplyProcessor { + using ApplyConfigurationSetResult = Configuration::ApplyConfigurationSetResult; using ConfigurationSet = Configuration::ConfigurationSet; using ConfigurationUnit = Configuration::ConfigurationUnit; using ConfigurationUnitResultInformation = Configuration::ConfigurationUnitResultInformation; @@ -24,11 +26,14 @@ namespace winrt::Microsoft::Management::Configuration::implementation using result_type = decltype(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationSetResult>>()); - ConfigurationSetApplyProcessor(const ConfigurationSet& configurationSet, const TelemetryTraceLogger& telemetry, IConfigurationSetProcessor&& setProcessor, result_type result, const std::function<void(ConfigurationSetChangeData)>& progress); + ConfigurationSetApplyProcessor(const ConfigurationSet& configurationSet, const TelemetryTraceLogger& telemetry, IConfigurationSetProcessor&& setProcessor, AppInstaller::WinRT::AsyncProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData>&& progress); // Processes the apply for the configuration set. void Process(); + // Gets the result object. + ApplyConfigurationSetResult Result() const; + private: // Contains all of the relevant data for a configuration unit. struct UnitInfo @@ -92,11 +97,14 @@ namespace winrt::Microsoft::Management::Configuration::implementation void SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo); void SendProgressIfNotComplete(ConfigurationUnitState state, const UnitInfo& unitInfo); + // For exception telemetry, get our internal status + TelemetryTraceLogger::ProcessingSummaryForIntent GetProcessingSummaryFor(ConfigurationUnitIntent intent) const; + ConfigurationSet m_configurationSet; IConfigurationSetProcessor m_setProcessor; const TelemetryTraceLogger& m_telemetry; result_type m_result; - std::function<void(ConfigurationSetChangeData)> m_progress; + AppInstaller::WinRT::AsyncProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> m_progress; std::vector<UnitInfo> m_unitInfo; std::map<std::string, size_t> m_idToUnitInfoIndex; hresult m_resultCode; diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp @@ -16,40 +16,6 @@ using namespace AppInstaller::YAML; namespace winrt::Microsoft::Management::Configuration::implementation { - namespace - { - std::string StreamToString(const Windows::Storage::Streams::IInputStream& stream) - { - uint32_t bufferSize = 1 << 20; - Windows::Storage::Streams::Buffer buffer(bufferSize); - Windows::Storage::Streams::InputStreamOptions readOptions = Windows::Storage::Streams::InputStreamOptions::Partial | Windows::Storage::Streams::InputStreamOptions::ReadAhead; - std::string result; - - for (;;) - { - Windows::Storage::Streams::IBuffer readBuffer = stream.ReadAsync(buffer, bufferSize, readOptions).get(); - - size_t readSize = static_cast<size_t>(readBuffer.Length()); - if (readSize) - { - static_assert(sizeof(char) == sizeof(*readBuffer.data())); - result.append(reinterpret_cast<char*>(readBuffer.data()), readSize); - } - else - { - break; - } - } - - return result; - } - } - - std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::Create(const Windows::Storage::Streams::IInputStream& stream) - { - return Create(StreamToString(stream)); - } - std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::Create(std::string_view input) { AICLI_LOG_LARGE_STRING(Config, Verbose, << "Parsing configuration set:", input); diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser.h @@ -15,9 +15,6 @@ namespace winrt::Microsoft::Management::Configuration::implementation // Interface for parsing a configuration set stream. struct ConfigurationSetParser { - // Create a parser from the given stream. - static std::unique_ptr<ConfigurationSetParser> Create(const Windows::Storage::Streams::IInputStream& stream); - // Create a parser from the given bytes (the encoding is detected). static std::unique_ptr<ConfigurationSetParser> Create(std::string_view input); diff --git a/src/Microsoft.Management.Configuration/Telemetry/Telemetry.cpp b/src/Microsoft.Management.Configuration/Telemetry/Telemetry.cpp @@ -342,6 +342,23 @@ namespace winrt::Microsoft::Management::Configuration::implementation } CATCH_LOG(); + void TelemetryTraceLogger::LogConfigProcessingSummaryForTestException( + const ConfigurationSet& configurationSet, + hresult error, + const TestConfigurationSetResult& result) const noexcept try + { + if (!IsTelemetryEnabled()) + { + return; + } + + ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); + + LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.IsFromHistory(), ConfigurationUnitIntent::Assert, + error, ConfigurationUnitResultSource::Internal, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); + } + CATCH_LOG(); + void TelemetryTraceLogger::LogConfigProcessingSummaryForApply( const ConfigurationSet& configurationSet, const ApplyConfigurationSetResult& result) const noexcept try diff --git a/src/Microsoft.Management.Configuration/Telemetry/Telemetry.h b/src/Microsoft.Management.Configuration/Telemetry/Telemetry.h @@ -97,6 +97,12 @@ namespace winrt::Microsoft::Management::Configuration::implementation const ConfigurationSet& configurationSet, const TestConfigurationSetResult& result) const noexcept; + // Logs a processing summary event for a configuration set test run exception. + void LogConfigProcessingSummaryForTestException( + const ConfigurationSet& configurationSet, + hresult error, + const TestConfigurationSetResult& result) const noexcept; + // Logs a processing summary event for a configuration set apply run. void LogConfigProcessingSummaryForApply( const ConfigurationSet& configurationSet,