-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed self capture in TermControl #3908
Conversation
Thanks! For this one, actually, I’m wondering if we should use C++/WinRT’s auto_revoke machinery. Since we are largely dealing with other WinRT objects and not manually bound std::functions, we can probably use more of the cool platform accordances. These auto_revokers get torn down during destruction, which ensures that a destructed TermControl has all of its handlers unbound before it becomes invalid. |
(There are a couple where we explicitly specify the tear down order, but those shouldn’t need to be weak.) |
Novice stuff
I'll start by rolling back the auto_revoke handlers that I changed to use weak_ref (didn't realize auto_revoke made the event safe), and if I see some easy wins for the others I'll rework them to. |
Thanks 😄 |
WinRT has some neat features
Was able to make one extra event auto_revoke, so all events |
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with this. Thanks.
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { | ||
// Update our control settings | ||
_ApplyUISettings(); | ||
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you can [weakThis = get_weak()]
if the weak ref is only going to be used in the lambda
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That form reads better. I'll include it in the next commit.
if (auto control{ weakThis.get() }) | ||
{ | ||
// Update our control settings | ||
control->_ApplyUISettings(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not 100% sure on how i feel about indirecting through control->
every time; in debug builds each one will be a call to winrt::com_ptr::operator->()
...
but i do not know how much, if at all, I care. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(we could capture weakThis,this
and then only use this
implicitly if the weak got locked. @zadjii-msft?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the way the weakThis,this
lambdas read. All the control->
just clutters things, not mention your comment about winrt::com_ptr::operator->()
. WinRT could almost use a class to support this form of lambda. It would be extensible to all the places where we're having trouble with the { this, pointer-to-member }
event format. Anyways, I'll include the fix to these in the next revision commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I prefer it like this - not capturing this
at all makes it harder for us to accidentally do the wrong thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess there's a "convenient" middle ground...
if (auto strongThis{ weakThis.get() })
{
auto& that{ *strongThis };
that->x;
that->y;
}
I am worried about the constant indirection through operator->
... but at the same time, WRL::ComPtr
literally is that for all com calls, sooooooooo maybe i should not be worried?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just that this is a member function, but we have to write it in a weird way that makes it not look like a member function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know which form to go with, and I'll include it in the next commit.
localAutoScrollTimer.Stop(); | ||
// _autoScrollTimer timer, now stopped, is destroyed. | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm surprised tearing down the timers isn't necessary anymore, but I'm willing to believe it 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those Stop
calls were only throwing in the event that TermControl
was being destructed at the end of an event handler, which was strange. Could just wrap them in try-catch blocks if the Stop
calls really should be there 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most correct solution might be to make both the timers Tick
events take a get_weak()
as opposed to this
, right? If we had done that from the start, we probably wouldn't have ever needed these lines...
Thanks for the reviews. I've pushed some changes. |
I'm closing and reopening to convince the CLA bot. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with this, but maybe we should make those Tick
events take get_weak()
. @mkitzan are you planning on making a pass for all the { this, &TermControl::EventHandler }
instances?
localAutoScrollTimer.Stop(); | ||
// _autoScrollTimer timer, now stopped, is destroyed. | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most correct solution might be to make both the timers Tick
events take a get_weak()
as opposed to this
, right? If we had done that from the start, we probably wouldn't have ever needed these lines...
Yeah, |
Hello @zadjii-msft! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
🎉 Handy links: |
Summary of the Pull Request
An asynchronous event handler capturing raw
this
inTermControl
was causing an exception to be thrown when a scroll update event occurred after closing the active tab. This PR replaces all non-auto_revoke lambda captures inTermControl
to capture (and validate) awinrt::weak_ref
instead of using rawthis
.References
PR Checklist
Detailed Description of the Pull Request / Additional comments
TermControl
is already a WinRT type so no changes were required to enable thewinrt::weak_ref
functionality. There was only one strange change I had to make. In the destructor's helper functionClose
, I had to remove two calls toStop
which were throwing under some circumstances. Fortunately, these calls don't appear to be critical, but definitely a spot to look into when reviewing this PR.Beyond scrolling, any anomalous crash related to the following functionality while closing a tab or WT may be fixed by this PR:
Validation Steps Performed
Before these changes I was able to consistently repro the issue in #2947. Now, I can no longer repro the issue.