-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
Avoid timer ticks on frozen windows #16587
Conversation
@lhecker Is this a 1.18 backport candidate? |
The PR fails to build because |
} | ||
|
||
::winrt::Windows::UI::Xaml::DispatcherTimer _timer{ nullptr }; | ||
winrt::event_token _token; |
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.
somehow methinks there's an auto_revoke
we could use here or something, but also, meh?
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.
Yeah I think winrt::event_token
is better in this case. The auto_revoke
classes are effectively just a pair<weak_ref, event_token>
which, on destruction, turns it into a strong-ref and revokes it. Since this is such a tiny class, I think the extra work to get rid of the extra weak_ref
is worth it. (It's just like 2 lines of extra code after all).
return _timer && _timer.IsEnabled(); | ||
} | ||
|
||
void Tick(winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable> const& handler) |
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.
Okay so notably, SafeDispatcherTimer
only supports a single callback at a time for Tick
(whereas I'm pretty sure that the standard one supports many callbacks) (this is also fine, pretty sure we're not using multiple callbacks on a single timer anyways)
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.
My intention was that this avoids accidentally setting the same callback over and over again on the same timer instance. A number of places don't revoke the callbacks after all and just replace the timer instance without calling Stop()
(i.e. they leak the timer instances).
|
||
std::optional<Windows::UI::Xaml::DispatcherTimer> _cursorTimer; |
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.
thank you for killing these optional<nullable thing>
s
cursorTimer.Interval(std::chrono::milliseconds(blinkTime)); | ||
cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick }); | ||
_cursorTimer.emplace(std::move(cursorTimer)); | ||
_cursorTimer.Interval(std::chrono::milliseconds(blinkTime)); |
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.
note to self: Interval
will correctly re-initialize the underlying Timer
if it was already Destroy
'ed
|
||
#include <winrt/windows.ui.core.h> | ||
#include <winrt/Windows.UI.h> |
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.
jeepers I don't love this, presumably this should be in the pch for both this project and the tests, but also those tests really haven't had love in years. So, this is fine for now. (I can only hope that it also being in the pch.h for TerminalAppLib means it won't actually get re-parsed here)
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 Windows.UI
header is a smaller than the Windows.UI.Core
header so this at least makes it a tiny bit better.
At the time of writing, closing the last tab of a window inexplicably doesn't lead to the destruction of the remaining TermControl instance. On top of that, on Win10 we don't destroy window threads due to bugs in DesktopWindowXamlSource. In other words, we leak TermControl instances. Additionally, the XAML timer class is "self-referential". Releasing all references to an instance will not stop the timer. Only calling Stop() explicitly will achieve that. The result is that the message loop of a frozen window thread has so far received 1-2 messages per second due to the blink timer not being stopped. This may have filled the message queue and lead to bugs as described in #16332 where keyboard input stopped working. (cherry picked from commit 521a300) Service-Card-Id: 91620224 Service-Version: 1.18
At the time of writing, closing the last tab of a window inexplicably doesn't lead to the destruction of the remaining TermControl instance. On top of that, on Win10 we don't destroy window threads due to bugs in DesktopWindowXamlSource. In other words, we leak TermControl instances. Additionally, the XAML timer class is "self-referential". Releasing all references to an instance will not stop the timer. Only calling Stop() explicitly will achieve that. The result is that the message loop of a frozen window thread has so far received 1-2 messages per second due to the blink timer not being stopped. This may have filled the message queue and lead to bugs as described in #16332 where keyboard input stopped working. (cherry picked from commit 521a300) Service-Card-Id: 91642474 Service-Version: 1.19
This changeset ensures that the message queue of frozen windows is always being serviced. This should ensure that it won't fill up and lead to deadlocks, freezes, or similar. I've tried _a lot_ of different approaches before settling on this one. Introducing a custom `WM_APP` message has the benefit of being the least intrusive to the existing code base. The approach that I would have favored the most would be to never destroy the `AppHost` instance in the first place, as I imagined that this would be more robust in general and resolve other (rare) bugs. However, I found that this requires rewriting some substantial parts of the code base around `AppHost` and it could be something that may be of interest in the future. Closes #16332 Depends on #16587 and #16575
This changeset ensures that the message queue of frozen windows is always being serviced. This should ensure that it won't fill up and lead to deadlocks, freezes, or similar. I've tried _a lot_ of different approaches before settling on this one. Introducing a custom `WM_APP` message has the benefit of being the least intrusive to the existing code base. The approach that I would have favored the most would be to never destroy the `AppHost` instance in the first place, as I imagined that this would be more robust in general and resolve other (rare) bugs. However, I found that this requires rewriting some substantial parts of the code base around `AppHost` and it could be something that may be of interest in the future. Closes #16332 Depends on #16587 and #16575 (cherry picked from commit 5d2fa47) Service-Card-Id: 91642478 Service-Version: 1.18
This changeset ensures that the message queue of frozen windows is always being serviced. This should ensure that it won't fill up and lead to deadlocks, freezes, or similar. I've tried _a lot_ of different approaches before settling on this one. Introducing a custom `WM_APP` message has the benefit of being the least intrusive to the existing code base. The approach that I would have favored the most would be to never destroy the `AppHost` instance in the first place, as I imagined that this would be more robust in general and resolve other (rare) bugs. However, I found that this requires rewriting some substantial parts of the code base around `AppHost` and it could be something that may be of interest in the future. Closes #16332 Depends on #16587 and #16575 (cherry picked from commit 5d2fa47) Service-Card-Id: 91642479 Service-Version: 1.19
At the time of writing, closing the last tab of a window inexplicably
doesn't lead to the destruction of the remaining TermControl instance.
On top of that, on Win10 we don't destroy window threads due to bugs in
DesktopWindowXamlSource. In other words, we leak TermControl instances.
Additionally, the XAML timer class is "self-referential".
Releasing all references to an instance will not stop the timer.
Only calling Stop() explicitly will achieve that.
The result is that the message loop of a frozen window thread has so
far received 1-2 messages per second due to the blink timer not being
stopped. This may have filled the message queue and lead to bugs as
described in #16332 where keyboard input stopped working.