Skip to content

Commit 5ed42dd

Browse files
authored
Port window thread shutdown to 1.11 (#5782)
Ports #5781 to the 1.11 branch.
1 parent cf4eb46 commit 5ed42dd

File tree

3 files changed

+79
-10
lines changed

3 files changed

+79
-10
lines changed

src/AppInstallerCLICore/Commands/TestCommand.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "Workflows/ConfigurationFlow.h"
1212
#include <winget/RepositorySource.h>
1313
#include <winrt/Microsoft.Management.Configuration.h>
14+
#include <winrt/Windows.ApplicationModel.Core.h>
1415

1516
using namespace AppInstaller::CLI::Workflow;
1617
using namespace AppInstaller::Utility::literals;
@@ -72,7 +73,7 @@ namespace AppInstaller::CLI
7273
NULL,
7374
ENDSESSION_CLOSEAPP,
7475
(SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT),
75-
5000,
76+
10000,
7677
NULL));
7778
}
7879

@@ -275,6 +276,39 @@ namespace AppInstaller::CLI
275276
return module ? module->GetObjectCount() : 0;
276277
}
277278
};
279+
280+
struct TestTerminateTerminationSignalHandler final : public Command
281+
{
282+
TestTerminateTerminationSignalHandler(std::string_view parent) : Command("term-signal-handler", {}, parent, Visibility::Hidden) {}
283+
284+
Resource::LocString ShortDescription() const override
285+
{
286+
return "Test TerminationSignalHandler thread"_lis;
287+
}
288+
289+
Resource::LocString LongDescription() const override
290+
{
291+
return "Forces the TerminationSignalHandler static object to be destroyed so that the thread behavior can be observed."_lis;
292+
}
293+
294+
protected:
295+
void ExecuteInternal(Execution::Context& context) const override
296+
{
297+
// Destroy the one created by standard execution
298+
// We join on the window thread, so if this never exits we have failed the test.
299+
winrt::Windows::ApplicationModel::Core::CoreApplication::Properties().TryRemove(L"WindowsPackageManager.SignalTerminationHandler");
300+
301+
// Create a new instance
302+
if (Execution::GetWindowHandle() == nullptr)
303+
{
304+
LogAndReport(context, "Didn't get a window handle");
305+
}
306+
else
307+
{
308+
LogAndReport(context, "Got a window handle");
309+
}
310+
}
311+
};
278312
}
279313

280314
std::vector<std::unique_ptr<Command>> TestCommand::GetCommands() const
@@ -285,6 +319,7 @@ namespace AppInstaller::CLI
285319
std::make_unique<TestConfigurationExportCommand>(FullName()),
286320
std::make_unique<TestConfigurationFindUnitProcessorsCommand>(FullName()),
287321
std::make_unique<TestCanUnloadNowCommand>(FullName()),
322+
std::make_unique<TestTerminateTerminationSignalHandler>(FullName()),
288323
});
289324
}
290325

src/AppInstallerCLICore/ExecutionContext.cpp

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <winget/NetworkSettings.h>
1414

1515
using namespace AppInstaller::Checkpoints;
16+
using namespace std::chrono_literals;
1617

1718
namespace AppInstaller::CLI::Execution
1819
{
@@ -117,11 +118,16 @@ namespace AppInstaller::CLI::Execution
117118

118119
~SignalTerminationHandler()
119120
{
120-
// At this point the thread is gone, but it will get angry
121-
// if there's no call to join.
121+
// std::thread requires that any managed thread (joinable) be joined or detached before destructing
122122
if (m_windowThread.joinable())
123123
{
124-
m_windowThread.detach();
124+
if (m_windowHandle)
125+
{
126+
// Inform the thread that it should stop.
127+
PostMessageW(m_windowHandle.get(), WM_DESTROY, 0, 0);
128+
}
129+
130+
m_windowThread.join();
125131
}
126132
}
127133

@@ -219,6 +225,12 @@ namespace AppInstaller::CLI::Execution
219225
return;
220226
}
221227

228+
// Unregister the window class on exiting the thread
229+
auto classUnregister = wil::scope_exit([&]()
230+
{
231+
UnregisterClassW(windowClass, hInstance);
232+
});
233+
222234
m_windowHandle = wil::unique_hwnd(CreateWindow(
223235
windowClass,
224236
L"WingetMessageOnlyWindow",
@@ -232,26 +244,38 @@ namespace AppInstaller::CLI::Execution
232244
hInstance,
233245
NULL)); /* lpParam */
234246

235-
if (m_windowHandle == nullptr)
247+
HWND windowHandle = m_windowHandle.get();
248+
if (windowHandle == nullptr)
236249
{
237250
LOG_LAST_ERROR_MSG("Failed creating window");
238251
return;
239252
}
240253

241-
ShowWindow(m_windowHandle.get(), SW_HIDE);
254+
// We must destroy the window first so that the class unregister can succeed
255+
auto destroyWindow = wil::scope_exit([&]()
256+
{
257+
DestroyWindow(windowHandle);
258+
});
259+
260+
ShowWindow(windowHandle, SW_HIDE);
242261

243262
// Force message queue to be created.
244263
MSG msg;
245264
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
246265
m_messageQueueReady.SetEvent();
247266

248-
// Message loop
267+
// Message loop, we send WM_DESTROY to terminate it
249268
BOOL getMessageResult;
250-
while ((getMessageResult = GetMessage(&msg, m_windowHandle.get(), 0, 0)) != 0)
269+
while ((getMessageResult = GetMessage(&msg, windowHandle, 0, 0)) != 0)
251270
{
252271
if (getMessageResult == -1)
253272
{
254273
LOG_LAST_ERROR();
274+
break;
275+
}
276+
else if (msg.message == WM_DESTROY)
277+
{
278+
break;
255279
}
256280
else
257281
{

src/AppInstallerCLIE2ETests/AppShutdownTests.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// -----------------------------------------------------------------------------
1+
// -----------------------------------------------------------------------------
22
// <copyright file="AppShutdownTests.cs" company="Microsoft Corporation">
33
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
44
// </copyright>
@@ -121,5 +121,15 @@ public void CanUnloadNowTest()
121121
Assert.True(lines[3].Contains("Internal objects: 0"));
122122
Assert.True(lines[4].Contains("External objects: 0"));
123123
}
124+
125+
/// <summary>
126+
/// Runs winget test term-signal-handler to check for proper thread termination.
127+
/// </summary>
128+
[Test]
129+
public void TermSignalHandler()
130+
{
131+
var result = TestCommon.RunAICLICommand("test", "term-signal-handler --verbose");
132+
Assert.True(result.StdOut.Contains("Got a window handle"));
133+
}
124134
}
125-
}
135+
}

0 commit comments

Comments
 (0)