Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/AppInstallerCLICore/Public/ShutdownMonitoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ namespace AppInstaller::ShutdownMonitoring
// Add or remove the listener based on `enabled`.
static void EnableListener(bool enabled, ICancellable* cancellable);

// Gets whether the signal handler is enabled.
static bool Enabled();

// Sets whether the signal handler is enabled; the default is true.
// When set to false, the signal handler instance will not create signal listeners when created.
static void Enabled(bool enabled);

#ifndef AICLI_DISABLE_TEST_HOOKS
// Gets the window handle for the message window.
HWND GetWindowHandle() const;
Expand Down
18 changes: 18 additions & 0 deletions src/AppInstallerCLICore/ShutdownMonitoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ using namespace std::chrono_literals;

namespace AppInstaller::ShutdownMonitoring
{
static std::atomic_bool s_TerminationSignalHandlerEnabled = true;

std::shared_ptr<TerminationSignalHandler> TerminationSignalHandler::Instance()
{
struct Singleton : public WinRT::COMStaticStorageBase<TerminationSignalHandler>
Expand Down Expand Up @@ -58,6 +60,16 @@ namespace AppInstaller::ShutdownMonitoring
}
}

bool TerminationSignalHandler::Enabled()
{
return s_TerminationSignalHandlerEnabled;
}

void TerminationSignalHandler::Enabled(bool enabled)
{
s_TerminationSignalHandlerEnabled = enabled;
}

#ifndef AICLI_DISABLE_TEST_HOOKS
HWND TerminationSignalHandler::GetWindowHandle() const
{
Expand All @@ -76,6 +88,12 @@ namespace AppInstaller::ShutdownMonitoring
m_appShutdownEvent.create();
#endif

if (!s_TerminationSignalHandlerEnabled)
{
AICLI_LOG(CLI, Info, << "TerminationSignalHandler is disabled, skipping creation of signal listeners");
return;
}

if (Runtime::IsRunningInPackagedContext())
{
// Create package update listener
Expand Down
30 changes: 30 additions & 0 deletions src/AppInstallerCLIE2ETests/InprocTestbedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,29 @@ public void TypeName_Tests(bool freeCachedFactories, bool leakCOM, UnloadBehavio
});
}

/// <summary>
/// Tests that disable the termination signal handling.
/// </summary>
/// <param name="disableTerminationSignals">Control whether the module should listen to termination signals.</param>
/// <param name="unloadBehavior">Set the unload behavior for the test.</param>
/// <param name="workTestSleep">Sets the number of milliseconds to sleep between each work/test iteration.</param>
[Test]
[TestCase(true, UnloadBehavior.Allow, 1000)]
[TestCase(true, UnloadBehavior.Never)]
[TestCase(false, UnloadBehavior.Allow, 1000)]
[TestCase(false, UnloadBehavior.Never)]
public void TerminationSignal_Tests(bool disableTerminationSignals, UnloadBehavior unloadBehavior, int? workTestSleep = null)
{
this.RunInprocTestbed(new TestbedParameters()
{
ActivationType = ActivationType.CoCreateInstance,
DisableTerminationSignals = disableTerminationSignals,
UnloadBehavior = unloadBehavior,
Iterations = 10,
WorkTestSleepInterval = workTestSleep,
});
}

private void RunInprocTestbed(TestbedParameters parameters, int timeout = 300000)
{
string builtParameters = string.Empty;
Expand Down Expand Up @@ -184,6 +207,11 @@ private void RunInprocTestbed(TestbedParameters parameters, int timeout = 300000
builtParameters += $"-work-test-sleep {parameters.WorkTestSleepInterval} ";
}

if (parameters.DisableTerminationSignals)
{
builtParameters += $"-no-term ";
}

var result = TestCommon.RunProcess(this.InprocTestbedPath, this.TargetPackageInformation, builtParameters, null, timeout, true);
Assert.AreEqual(0, result.ExitCode);
}
Expand All @@ -206,6 +234,8 @@ private class TestbedParameters
internal int? Iterations { get; init; } = null;

internal int? WorkTestSleepInterval { get; init; } = null;

internal bool DisableTerminationSignals { get; init; } = false;
}
}
}
5 changes: 5 additions & 0 deletions src/ComInprocTestbed/PackageManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ void SetUnloadPreference(bool value)
PackageManagerSettings settings;
settings.CanUnloadPreference(value);
}
void SetDisableTerminationSignals(bool value)
{
PackageManagerSettings settings;
settings.TerminationSignalMonitoring(!value);
}

bool DetectForSystem(const TestParameters& testParameters)
{
Expand Down
3 changes: 3 additions & 0 deletions src/ComInprocTestbed/PackageManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ void InitializePackageManagerGlobals();
// Sets the module to prevent it from unloading.
void SetUnloadPreference(bool value);

// Sets the module to prevent it from listening to termination signals.
void SetDisableTerminationSignals(bool value);

// Attempts to detect the target package as installed for the system.
bool DetectForSystem(const TestParameters& testParameters);

Expand Down
33 changes: 29 additions & 4 deletions src/ComInprocTestbed/Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ namespace
}
}

return result;
}

// Look for the set of termination signal monitoring objects that should be present after we have spun everything up.
// Returns true if all objects are found in the expected state.
bool SearchForTerminationSignalObjects(bool expectExist)
{
bool result = true;

// Shutdown monitoring window
bool foundWindow = false;
EnumWindows(CheckForWinGetWindow, reinterpret_cast<LPARAM>(&foundWindow));
Expand Down Expand Up @@ -251,6 +260,10 @@ TestParameters::TestParameters(int argc, const char** argv)
ADVANCE_ARG_PARAMETER
WorkTestSleepInterval = atoi(argv[i]);
}
else if ("-no-term"sv == argv[i])
{
DisableTerminationSignals = true;
}
}
}

Expand Down Expand Up @@ -290,8 +303,6 @@ bool TestParameters::InitializeTestState() const
return false;
}

InitializePackageManagerGlobals();

if (UnloadBehavior::Never == UnloadBehavior || UnloadBehavior::AtUninitialize == UnloadBehavior)
{
SetUnloadPreference(false);
Expand All @@ -300,6 +311,18 @@ bool TestParameters::InitializeTestState() const
return true;
}

bool TestParameters::InitializeIterationState() const
{
InitializePackageManagerGlobals();

if (DisableTerminationSignals)
{
SetDisableTerminationSignals(true);
}

return true;
}

std::unique_ptr<ITest> TestParameters::CreateTest() const
{
if ("unload_check"sv == TestToRun)
Expand Down Expand Up @@ -435,7 +458,8 @@ bool UnloadAndCheckForLeaks::RunIterationTest()
std::cout << "UnloadAndCheckForLeaks::RunIterationTest\n";

Snapshot beforeUnload;
if (!SearchForWellKnownObjects(true, beforeUnload))
if (!SearchForWellKnownObjects(true, beforeUnload) ||
!SearchForTerminationSignalObjects(!m_parameters.DisableTerminationSignals))
{
return false;
}
Expand All @@ -445,7 +469,8 @@ bool UnloadAndCheckForLeaks::RunIterationTest()
Snapshot afterUnload;
m_iterationSnapshots.emplace_back(beforeUnload, afterUnload);

if (!SearchForWellKnownObjects(!m_parameters.UnloadExpected(), afterUnload))
if (!SearchForWellKnownObjects(!m_parameters.UnloadExpected(), afterUnload) ||
!SearchForTerminationSignalObjects(!m_parameters.UnloadExpected() && !m_parameters.DisableTerminationSignals))
{
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/ComInprocTestbed/Tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct TestParameters

bool InitializeTestState() const;

bool InitializeIterationState() const;

std::unique_ptr<ITest> CreateTest() const;

void UninitializeTestState() const;
Expand All @@ -75,6 +77,7 @@ struct TestParameters
ActivationType ActivationType = ActivationType::ClassName;
bool SkipClearFactories = false;
DWORD WorkTestSleepInterval = 0;
bool DisableTerminationSignals = false;
};

// Captures a snapshot of current resource usage.
Expand Down
5 changes: 5 additions & 0 deletions src/ComInprocTestbed/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ int main(int argc, const char** argv) try
{
std::cout << "Begin iteration " << (i + 1) << std::endl;

if (!testParameters.InitializeIterationState())
{
return 2;
}

if (test && !test->RunIterationWork())
{
return 3;
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Management.Deployment/CanUnload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace winrt::Microsoft::Management::Deployment::implementation
{
static bool s_canUnload = true;
static std::atomic_bool s_canUnload = true;

void SetCanUnload(bool value)
{
Expand Down
17 changes: 12 additions & 5 deletions src/Microsoft.Management.Deployment/PackageManager.idl
Original file line number Diff line number Diff line change
Expand Up @@ -1696,12 +1696,19 @@ namespace Microsoft.Management.Deployment

[contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 28)]
{
// Gets or sets a value indicating whether the caller would prefer the module to stay loaded or not.
// This affects how the DllCanUnloadNow function called by COM behaves. If set to false it will act as if
// there are active objects at all times. If set to true it will allow the unload when there are no
// active objects.
// Defaults to true.
/// Gets or sets a value indicating whether the caller would prefer the module to stay loaded or not.
/// This affects how the DllCanUnloadNow function called by COM behaves. If set to false it will act as if
/// there are active objects at all times. If set to true it will allow the unload when there are no
/// active objects.
/// Defaults to true.
Boolean CanUnloadPreference{ get; set; };

/// Gets or sets a value indicating whether the module should listen for termination signals (CTRL+C, window messages, package updates)
/// and begin the process of cancelling active operations and preventing new ones.
/// If set to false, the caller is responsible for handling these termination signals and cancelling active operations as necessary.
/// Set this to the desired state before any PackageManager operations. Changing it after the first operation for the process may have undefined behavior.
/// Defaults to true.
Boolean TerminationSignalMonitoring{ get; set; };
}
}

Expand Down
13 changes: 12 additions & 1 deletion src/Microsoft.Management.Deployment/PackageManagerSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "PackageManagerSettings.g.cpp"
#include "Helpers.h"
#include "Public/CanUnload.h"
#include "Public/ShutdownMonitoring.h"
#include <winget/UserSettings.h>
#include <AppInstallerRuntime.h>

Expand Down Expand Up @@ -65,7 +66,17 @@ namespace winrt::Microsoft::Management::Deployment::implementation

void PackageManagerSettings::CanUnloadPreference(bool value)
{
return SetCanUnload(value);
SetCanUnload(value);
}

bool PackageManagerSettings::TerminationSignalMonitoring() const
{
return AppInstaller::ShutdownMonitoring::TerminationSignalHandler::Enabled();
}

void PackageManagerSettings::TerminationSignalMonitoring(bool value)
{
AppInstaller::ShutdownMonitoring::TerminationSignalHandler::Enabled(value);
}

CoCreatableMicrosoftManagementDeploymentClass(PackageManagerSettings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation
// Contract 28
bool CanUnloadPreference() const;
void CanUnloadPreference(bool value);
bool TerminationSignalMonitoring() const;
void TerminationSignalMonitoring(bool value);
};
}

Expand Down
Loading