-
Notifications
You must be signed in to change notification settings - Fork 29.7k
[win32] Do not flood message loop with wake up messages #179538
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
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
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.
Code Review
This pull request aims to prevent flooding the Windows message loop with wake-up messages by using an atomic flag to coalesce multiple WakeUp calls into a single WM_NULL message. While this successfully addresses the message flooding issue, the current implementation appears to re-introduce a critical bug that can lead to UI starvation. My review includes one critical comment detailing this issue and its implications.
| wake_up_posted_ = false; | ||
| ProcessTasks(); |
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.
This change introduces a potential for UI starvation. By resetting wake_up_posted_ to false before calling ProcessTasks(), you allow a task running inside ProcessTasks() to trigger a new WakeUp() call that will immediately post another WM_NULL message. If tasks repeatedly schedule immediate follow-up work (a common pattern with Future.delayed(Duration.zero)), this can create a tight loop of processing WM_NULL messages, preventing the message loop from handling other messages like user input and making the application unresponsive. This appears to re-introduce the problem that the original timer-based workaround was trying to solve.
To fix this, wake_up_posted_ should be reset after ProcessTasks(). However, this would introduce a "lost wakeup" problem if another thread calls WakeUp() while ProcessTasks() is executing. This race condition would also need to be addressed for the solution to be correct.
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 was the assumption originally. However if we make sure not to flood the message queue with unnecessary messages while ProcessTasks() is already pending the event messages are pumped correctly.
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.
Should we update wake up posted after processing tasks though?
In other words if ProcessTasks asks for another wakeup, should we ignore that?
It might be worth a quick comment to explain this.
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.
No. When wake up is requested during ProcessTasks() we need to schedule another wake-up message. Otherwise we would lose the wake-up request.
Let's say that dart code executed in ProcessTasks() calls Future.delayed(Duration.zero, () { ... }). If we update wake_up_posted_ after processing tasks, this will add the task to queue, but will not wake up the message loop. So the tasks will be only processed after something else wakes up the message loop (i.e. vsync waiter).
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 original assumption that PostMessage always takes precedence before event messages does not seem to be correct. What was actually happening is that for every wake up request we have been posting wake up message, but then processed all scheduled tasks when handling just the first message alone. This might in turns schedules another task, but the task is performed too early (because there are other wake-up messages still in queue).
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.
Thanks for the excellent explanations!
79a6238 to
3dfcf9e
Compare
62aa5e8 to
ab3331a
Compare
|
@knopp one thing that's not clear to me - why does the Otherwise, this change LGTM! |
|
It passed because the 1ms delay introduced in #179249 was enough to make the test, but not always enough when dart was flooding the message loop with wake-up messages. I think there should be a way to add test for the behavior introduced here (multiple wake-ups resulting in single message). I'll look into that. |
ab3331a to
6b15151
Compare
|
@loic-sharma, I added a test. It is a bit rudimentary but at least should prevent regressions (and it fails without the fix). |
0a7dcd8 to
3063191
Compare
loic-sharma
left a comment
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.
Thanks for fixing this, great work!
Fixes #173843
If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.