Translate all Dispatcher().RunAsync([](){}) lambdas into coroutines #5517

Closed
opened 2026-01-31 00:15:15 +00:00 by claunia · 6 comments
Owner

Originally created by @DHowett-MSFT on GitHub (Dec 11, 2019).

I believe there is a trivial mechanical translation that'll get us better code clarity and perhaps a better understanding of the flow of ownership through our code.

from

void TerminalPage::_SetFocusedTabIndex(int tabIndex)
{
    auto tab = _tabs.at(tabIndex);
    auto weakThis{ get_weak() };
    _tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tab, weakThis]() {
        if (auto page{ weakThis.get() })
        {
            page->_tabView.SelectedItem(tab->GetTabViewItem());
        }
    });
}

to

winrt::fire_and_forget TerminalPage::_SetFocusedTabIndex(int tabIndex)
{
    // GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex)
    //          sometimes set focus to an incorrect tab after removing some tabs
    auto tab = _tabs.at(tabIndex);
    auto weakThis{ get_weak() };

    co_await winrt::resume_foreground(_tabView.Dispatcher()); // transition to the dispatcher thread

    if (auto page{ weakThis.get() })
    {
        page->_tabView.SelectedItem(tab->GetTabViewItem());
    }
}

There's a bunch more information about concurrency and coroutines in C++/WinRT here.

Originally created by @DHowett-MSFT on GitHub (Dec 11, 2019). I believe there is a trivial mechanical translation that'll get us better code clarity and perhaps a better understanding of the flow of ownership through our code. ### from ```c++ void TerminalPage::_SetFocusedTabIndex(int tabIndex) { auto tab = _tabs.at(tabIndex); auto weakThis{ get_weak() }; _tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tab, weakThis]() { if (auto page{ weakThis.get() }) { page->_tabView.SelectedItem(tab->GetTabViewItem()); } }); } ``` ### to ```c++ winrt::fire_and_forget TerminalPage::_SetFocusedTabIndex(int tabIndex) { // GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex) // sometimes set focus to an incorrect tab after removing some tabs auto tab = _tabs.at(tabIndex); auto weakThis{ get_weak() }; co_await winrt::resume_foreground(_tabView.Dispatcher()); // transition to the dispatcher thread if (auto page{ weakThis.get() }) { page->_tabView.SelectedItem(tab->GetTabViewItem()); } } ``` There's a bunch more information about concurrency and coroutines in C++/WinRT [here](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/concurrency-2).
Author
Owner

@DHowett-MSFT commented on GitHub (Dec 11, 2019):

Not to be in the business of nominating community members out of the blue to do labor on our behalf, but I really think @mkitzan might be great here: he's looked at a lot of ownership issues and dispatch queues lately, and this could be right up his alley.

@DHowett-MSFT commented on GitHub (Dec 11, 2019): Not to be in the business of nominating community members out of the blue to do labor on our behalf, but I really think @mkitzan might be great here: he's looked at a lot of ownership issues and dispatch queues lately, and this could be right up his alley.
Author
Owner

@zadjii-msft commented on GitHub (Dec 11, 2019):

Overall this does look better, though I'd want to be careful with this - the first pattern is a little more obvious when we're accidentally using this within the body of the dispatched lambda, while the second will make it easier for us to forget that we shouldn't be using this there.

Unless there's also another way for us to resume_foreground_unless_this_goes_away or something

@zadjii-msft commented on GitHub (Dec 11, 2019): Overall this does look better, though I'd want to be careful with this - the first pattern is a little more obvious when we're accidentally using `this` within the body of the dispatched lambda, while the second will make it easier for us to forget that we shouldn't be using `this` there. Unless there's also another way for us to `resume_foreground_unless_this_goes_away` or something
Author
Owner

@DHowett-MSFT commented on GitHub (Dec 11, 2019):

To be fair, using this is totally valid after the weak ref is locked. It may even be preferred as it lets us avoid having to access member variables on someOtherObject->

@DHowett-MSFT commented on GitHub (Dec 11, 2019): To be fair, using `this` is totally valid after the weak ref is locked. It may even be preferred as it lets us avoid having to access member variables on `someOtherObject->`
Author
Owner

@DHowett-MSFT commented on GitHub (Dec 11, 2019):

There's some suggestions here, actually: instead of making these weak, we can make them strong and perform the capture before the co_await.

Safely accessing the this pointer in a class-member coroutine

@DHowett-MSFT commented on GitHub (Dec 11, 2019): There's some suggestions here, actually: instead of making these _weak_, we can make them _strong_ and perform the capture before the `co_await`. [Safely accessing the _this_ pointer in a class-member coroutine](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/weak-references#safely-accessing-the-this-pointer-in-a-class-member-coroutine)
Author
Owner

@DHowett-MSFT commented on GitHub (Dec 11, 2019):

Oh my gosh, it gets even better. Check this out!

For a weak reference, call get_weak. C++/WinRT ensures that the resulting delegate holds a weak reference. At the last minute, and behind the scenes, the delegate attempts to resolve the weak reference to a strong one, and only calls the member function if it's successful.

event_source.Event({ get_weak(), &EventRecipient::OnEvent });
@DHowett-MSFT commented on GitHub (Dec 11, 2019): Oh my gosh, it gets even better. Check this out! > For a weak reference, call get_weak. C++/WinRT ensures that the resulting delegate holds a weak reference. At the last minute, and behind the scenes, the delegate attempts to resolve the weak reference to a strong one, and only calls the member function if it's successful. ``` event_source.Event({ get_weak(), &EventRecipient::OnEvent }); ```
Author
Owner

@mkitzan commented on GitHub (Dec 11, 2019):

Woah WinRT has it all! I'd be up to rework the Dispatcher().RunAsync calls to whatever pattern is decided on. I think the co routine pattern + get_strong() would be the most readable/understandable.
Also, I've been trying to turn one of the TermControl instances to the { get_weak(), &EventRecipient::OnEvent } form. However, the pointer to member form doesn't capture information about the arguments to provide the function and probably has to have specific parameters.

@mkitzan commented on GitHub (Dec 11, 2019): Woah WinRT has it all! I'd be up to rework the `Dispatcher().RunAsync` calls to whatever pattern is decided on. I think the co routine pattern + get_strong() would be the most readable/understandable. ~Also, I've been trying to turn one of the `TermControl` instances to the `{ get_weak(), &EventRecipient::OnEvent }` form. However, the pointer to member form doesn't capture information about the arguments to provide the function and probably has to have specific parameters.~
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#5517