Skip to content

Commit a0724f9

Browse files
lheckerDHowett
authored andcommitted
Use smart pointers in coroutines (#19752)
After the coroutines resume, `this` may have already been destroyed. So, this PR adds proper use of `get_weak/strong`. Closes #19745 (cherry picked from commit a528141) Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgjzPAU Service-Version: 1.23
1 parent 9fa8d38 commit a0724f9

10 files changed

Lines changed: 156 additions & 32 deletions

File tree

src/cascadia/TerminalApp/AppActionHandlers.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,10 @@ namespace winrt::TerminalApp::implementation
898898
co_return;
899899
}
900900

901-
// Hop to the BG thread
901+
// ShellExecuteExW may block, so do it on a background thread.
902+
//
903+
// NOTE: All remaining code of this function doesn't touch `this`, so we don't need weak/strong_ref.
904+
// NOTE NOTE: Don't touch `this` when you make changes here.
902905
co_await winrt::resume_background();
903906

904907
// This will get us the correct exe for dev/preview/release. If you
@@ -1451,6 +1454,8 @@ namespace winrt::TerminalApp::implementation
14511454

14521455
safe_void_coroutine TerminalPage::_doHandleSuggestions(SuggestionsArgs realArgs)
14531456
{
1457+
const auto weak = get_weak();
1458+
const auto dispatcher = Dispatcher();
14541459
const auto source = realArgs.Source();
14551460
std::vector<Command> commandsCollection;
14561461
Control::CommandHistoryContext context{ nullptr };
@@ -1517,7 +1522,12 @@ namespace winrt::TerminalApp::implementation
15171522
}
15181523
}
15191524

1520-
co_await wil::resume_foreground(Dispatcher());
1525+
co_await wil::resume_foreground(dispatcher);
1526+
const auto strong = weak.get();
1527+
if (!strong)
1528+
{
1529+
co_return;
1530+
}
15211531

15221532
// Open the palette with all these commands in it.
15231533
_OpenSuggestions(_GetActiveControl(),

src/cascadia/TerminalApp/DebugTapConnection.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,15 @@ namespace winrt::Microsoft::TerminalApp::implementation
3434
// before actually starting the connection to the client app. This
3535
// will ensure both controls are initialized before the client app
3636
// is.
37+
const auto weak = get_weak();
3738
co_await winrt::resume_background();
38-
_pairedTap->_start.wait();
39+
const auto strong = weak.get();
40+
if (!strong)
41+
{
42+
co_return;
43+
}
3944

45+
_pairedTap->_start.wait();
4046
_wrappedConnection.Start();
4147
}
4248
void WriteInput(const winrt::array_view<const char16_t> buffer)

src/cascadia/TerminalApp/Jumplist.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ safe_void_coroutine Jumplist::UpdateJumplist(const CascadiaSettings& settings) n
7878
// make sure to capture the settings _before_ the co_await
7979
const auto strongSettings = settings;
8080

81+
// Explorer APIs may block, so do it on a background thread.
82+
//
83+
// NOTE: Jumplist has no members, so we don't need to hold onto `this` here.
8184
co_await winrt::resume_background();
8285

8386
try

src/cascadia/TerminalApp/SnippetsPaneContent.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,22 @@ namespace winrt::TerminalApp::implementation
4747
{
4848
_settings = settings;
4949

50+
const auto dispatcher = Dispatcher();
51+
const auto weak = get_weak();
52+
5053
// You'd think that `FilterToSendInput(queryString)` would work. It
5154
// doesn't! That uses the queryString as the current command the user
5255
// has typed, then relies on the suggestions UI to _also_ filter with that
5356
// string.
5457

5558
const auto tasks = co_await _settings.GlobalSettings().ActionMap().FilterToSnippets(winrt::hstring{}, winrt::hstring{}); // IVector<Model::Command>
56-
co_await wil::resume_foreground(Dispatcher());
59+
co_await wil::resume_foreground(dispatcher);
60+
61+
const auto strong = weak.get();
62+
if (!strong)
63+
{
64+
co_return;
65+
}
5766

5867
_allTasks.Clear();
5968
for (const auto& t : tasks)

src/cascadia/TerminalApp/TabManagement.cpp

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -403,12 +403,18 @@ namespace winrt::TerminalApp::implementation
403403
// - tab: the tab to remove
404404
winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::TabBase tab)
405405
{
406+
winrt::com_ptr<TerminalPage> strong;
407+
406408
if (tab.ReadOnly())
407409
{
410+
const auto weak = get_weak();
411+
408412
auto warningResult = co_await _ShowCloseReadOnlyDialog();
409413

414+
strong = weak.get();
415+
410416
// If the user didn't explicitly click on close tab - leave
411-
if (warningResult != ContentDialogResult::Primary)
417+
if (!strong || warningResult != ContentDialogResult::Primary)
412418
{
413419
co_return;
414420
}
@@ -719,10 +725,14 @@ namespace winrt::TerminalApp::implementation
719725
{
720726
if (pane->ContainsReadOnly())
721727
{
728+
const auto weak = get_weak();
729+
722730
auto warningResult = co_await _ShowCloseReadOnlyDialog();
723731

732+
const auto strong = weak.get();
733+
724734
// If the user didn't explicitly click on close tab - leave
725-
if (warningResult != ContentDialogResult::Primary)
735+
if (!strong || warningResult != ContentDialogResult::Primary)
726736
{
727737
co_return false;
728738
}
@@ -780,9 +790,13 @@ namespace winrt::TerminalApp::implementation
780790

781791
if (const auto pane{ terminalTab->GetActivePane() })
782792
{
793+
const auto weak = get_weak();
783794
if (co_await _PaneConfirmCloseReadOnly(pane))
784795
{
785-
_HandleClosePaneRequested(pane);
796+
if (const auto strong = weak.get())
797+
{
798+
_HandleClosePaneRequested(pane);
799+
}
786800
}
787801
}
788802
}
@@ -840,9 +854,22 @@ namespace winrt::TerminalApp::implementation
840854
// - tabs - tabs to remove
841855
safe_void_coroutine TerminalPage::_RemoveTabs(const std::vector<winrt::TerminalApp::TabBase> tabs)
842856
{
857+
const auto weak = get_weak();
858+
843859
for (auto& tab : tabs)
844860
{
845-
co_await _HandleCloseTabRequested(tab);
861+
winrt::Windows::Foundation::IAsyncAction action{ nullptr };
862+
if (const auto strong = weak.get())
863+
{
864+
action = _HandleCloseTabRequested(tab);
865+
}
866+
867+
if (!action)
868+
{
869+
co_return;
870+
}
871+
872+
co_await action;
846873
}
847874
}
848875
// Method Description:
@@ -919,18 +946,23 @@ namespace winrt::TerminalApp::implementation
919946

920947
safe_void_coroutine TerminalPage::_OnTabPointerReleasedCloseTab(winrt::Microsoft::UI::Xaml::Controls::TabViewItem sender)
921948
{
949+
// WinUI asynchronously updates its tab view items, so it may happen that we're given a
950+
// `TabViewItem` that still contains a `TabBase` which has actually already been removed.
951+
// First we must yield once, to flush out whatever TabView is currently doing.
952+
const auto weak = get_weak();
953+
co_await wil::resume_foreground(Dispatcher());
954+
const auto strong = weak.get();
955+
if (!strong)
956+
{
957+
co_return;
958+
}
959+
922960
const auto tab = _GetTabByTabViewItem(sender);
923961
if (!tab)
924962
{
925963
co_return;
926964
}
927965

928-
// WinUI asynchronously updates its tab view items, so it may happen that we're given a
929-
// `TabViewItem` that still contains a `TabBase` which has actually already been removed.
930-
// First we must yield once, to flush out whatever TabView is currently doing.
931-
const auto strong = get_strong();
932-
co_await wil::resume_foreground(Dispatcher());
933-
934966
// `tab.Shutdown()` in `_RemoveTab()` sets the content to null = This checks if the tab is closed.
935967
if (tab.Content())
936968
{

src/cascadia/TerminalApp/TerminalPage.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,7 +2044,15 @@ namespace winrt::TerminalApp::implementation
20442044
if (!_displayingCloseDialog)
20452045
{
20462046
_displayingCloseDialog = true;
2047+
2048+
const auto weak = get_weak();
20472049
auto warningResult = co_await _ShowQuitDialog();
2050+
const auto strong = weak.get();
2051+
if (!strong)
2052+
{
2053+
co_return;
2054+
}
2055+
20482056
_displayingCloseDialog = false;
20492057

20502058
if (warningResult != ContentDialogResult::Primary)
@@ -3084,8 +3092,12 @@ namespace winrt::TerminalApp::implementation
30843092
// - eventArgs: the arguments specifying how to set the progress indicator
30853093
safe_void_coroutine TerminalPage::_SetTaskbarProgressHandler(const IInspectable /*sender*/, const IInspectable /*eventArgs*/)
30863094
{
3095+
const auto weak = get_weak();
30873096
co_await wil::resume_foreground(Dispatcher());
3088-
SetTaskbarProgress.raise(*this, nullptr);
3097+
if (const auto strong = weak.get())
3098+
{
3099+
SetTaskbarProgress.raise(*this, nullptr);
3100+
}
30893101
}
30903102

30913103
// Method Description:
@@ -3158,6 +3170,12 @@ namespace winrt::TerminalApp::implementation
31583170
{
31593171
co_return;
31603172
}
3173+
3174+
const auto weak = get_weak();
3175+
const auto dispatcher = Dispatcher();
3176+
3177+
// All of the code until resume_foreground is static and
3178+
// doesn't touch `this`, so we don't need weak/strong_ref.
31613179
co_await winrt::resume_background();
31623180

31633181
// no packages were found, nothing to suggest
@@ -3175,7 +3193,12 @@ namespace winrt::TerminalApp::implementation
31753193
suggestions.emplace_back(fmt::format(FMT_COMPILE(L"winget install --id {} -s winget"), pkg.CatalogPackage().Id()));
31763194
}
31773195

3178-
co_await wil::resume_foreground(Dispatcher());
3196+
co_await wil::resume_foreground(dispatcher);
3197+
const auto strong = weak.get();
3198+
if (!strong)
3199+
{
3200+
co_return;
3201+
}
31793202

31803203
auto term = _GetActiveControl();
31813204
if (!term)
@@ -3255,6 +3278,9 @@ namespace winrt::TerminalApp::implementation
32553278
// UI) thread. This is IMPORTANT, because the Windows.Storage API's
32563279
// (used for retrieving the path to the file) will crash on the UI
32573280
// thread, because the main thread is a STA.
3281+
//
3282+
// NOTE: All remaining code of this function doesn't touch `this`, so we don't need weak/strong_ref.
3283+
// NOTE NOTE: Don't touch `this` when you make changes here.
32583284
co_await winrt::resume_background();
32593285

32603286
auto openFile = [](const auto& filePath) {
@@ -4633,12 +4659,18 @@ namespace winrt::TerminalApp::implementation
46334659
// - sender: the ICoreState instance containing the connection state
46344660
// Return Value:
46354661
// - <none>
4636-
safe_void_coroutine TerminalPage::_ConnectionStateChangedHandler(const IInspectable& sender, const IInspectable& /*args*/) const
4662+
safe_void_coroutine TerminalPage::_ConnectionStateChangedHandler(const IInspectable& sender, const IInspectable& /*args*/)
46374663
{
46384664
if (const auto coreState{ sender.try_as<winrt::Microsoft::Terminal::Control::ICoreState>() })
46394665
{
46404666
const auto newConnectionState = coreState.ConnectionState();
4667+
const auto weak = get_weak();
46414668
co_await wil::resume_foreground(Dispatcher());
4669+
const auto strong = weak.get();
4670+
if (!strong)
4671+
{
4672+
co_return;
4673+
}
46424674

46434675
_adjustProcessPriorityThrottled->Run();
46444676

src/cascadia/TerminalApp/TerminalPage.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ namespace winrt::TerminalApp::implementation
512512
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
513513
void _OpenElevatedWT(winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
514514

515-
safe_void_coroutine _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
515+
safe_void_coroutine _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
516516
void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
517517
void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
518518
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
@@ -528,7 +528,7 @@ namespace winrt::TerminalApp::implementation
528528

529529
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
530530
Windows::Foundation::IAsyncAction _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
531-
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::MatchResult>> _FindPackageAsync(hstring query);
531+
static Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::MatchResult>> _FindPackageAsync(hstring query);
532532

533533
void _WindowSizeChanged(const IInspectable sender, const winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args);
534534
void _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);

src/cascadia/TerminalApp/TerminalWindow.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,10 @@ namespace winrt::TerminalApp::implementation
322322
// - an IAsyncOperation with the dialog result
323323
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalWindow::ShowDialog(winrt::WUX::Controls::ContentDialog dialog)
324324
{
325+
const auto weak = get_weak();
326+
const auto dispatcher = _root->Dispatcher();
327+
const auto root = _root->XamlRoot();
328+
325329
// As mentioned on s_activeDialog, dismissing the active dialog is necessary.
326330
// We repeat it a few times in case the resume_foreground failed to work,
327331
// but I found that one iteration will always be enough in practice.
@@ -335,7 +339,7 @@ namespace winrt::TerminalApp::implementation
335339
s_activeDialog.Hide();
336340

337341
// Wait for the current dialog to be hidden.
338-
co_await wil::resume_foreground(_root->Dispatcher(), CoreDispatcherPriority::Low);
342+
co_await wil::resume_foreground(dispatcher, CoreDispatcherPriority::Low);
339343
}
340344

341345
// If two sources call ShowDialog() simultaneously, it may happen that both enter the above loop,
@@ -352,7 +356,7 @@ namespace winrt::TerminalApp::implementation
352356
// IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs.
353357
// Since we're hosting the dialog in a Xaml island, we need to connect it to the
354358
// xaml tree somehow.
355-
dialog.XamlRoot(_root->XamlRoot());
359+
dialog.XamlRoot(root);
356360

357361
// IMPORTANT: Set the requested theme of the dialog, because the
358362
// PopupRoot isn't directly in the Xaml tree of our root. So the dialog
@@ -366,14 +370,17 @@ namespace winrt::TerminalApp::implementation
366370
// theme on each element up to the root. We're relying a bit on Xaml's implementation
367371
// details here, but it does have the desired effect.
368372
// It's not enough to set the theme on the dialog alone.
369-
auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
370-
auto theme{ _settings.GlobalSettings().CurrentTheme() };
371-
auto requestedTheme{ theme.RequestedTheme() };
372-
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
373-
while (element)
373+
auto themingLambda{ [weak](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
374+
if (const auto strong = weak.get())
374375
{
375-
element.RequestedTheme(requestedTheme);
376-
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
376+
auto theme{ strong->_settings.GlobalSettings().CurrentTheme() };
377+
auto requestedTheme{ theme.RequestedTheme() };
378+
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
379+
while (element)
380+
{
381+
element.RequestedTheme(requestedTheme);
382+
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
383+
}
377384
}
378385
} };
379386

0 commit comments

Comments
 (0)