Skip to content

Conversation

@knopp
Copy link
Member

@knopp knopp commented Dec 6, 2025

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-assist bot 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.

@flutter-dashboard

This comment was marked as outdated.

@github-actions github-actions bot added engine flutter/engine related. See also e: labels. platform-windows Building on or for Windows specifically a: desktop Running on desktop labels Dec 6, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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.

Comment on lines +221 to 223
wake_up_posted_ = false;
ProcessTasks();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

@knopp knopp Dec 6, 2025

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).

Copy link
Member Author

@knopp knopp Dec 6, 2025

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).

Copy link
Member

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!

@knopp knopp force-pushed the win_main_loop_deadlock branch from 79a6238 to 3dfcf9e Compare December 6, 2025 17:24
@knopp knopp changed the title win32: Do not flood message loop with wake up messages [win32] Do not flood message loop with wake up messages Dec 6, 2025
@knopp knopp force-pushed the win_main_loop_deadlock branch from 62aa5e8 to ab3331a Compare December 7, 2025 11:46
@loic-sharma
Copy link
Member

loic-sharma commented Dec 9, 2025

@knopp one thing that's not clear to me - why does the FlutterWindowsEngineTest.TaskRunnerDoesNotDeadlock test pass before this change? Isn't that spamming the message loop with wake up messages?

Otherwise, this change LGTM!

@knopp
Copy link
Member Author

knopp commented Dec 9, 2025

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.

@knopp knopp force-pushed the win_main_loop_deadlock branch from ab3331a to 6b15151 Compare December 10, 2025 16:54
@knopp
Copy link
Member Author

knopp commented Dec 10, 2025

@loic-sharma, I added a test. It is a bit rudimentary but at least should prevent regressions (and it fails without the fix).

@knopp knopp force-pushed the win_main_loop_deadlock branch from 0a7dcd8 to 3063191 Compare December 12, 2025 16:37
Copy link
Member

@loic-sharma loic-sharma left a 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!

@knopp knopp enabled auto-merge December 12, 2025 17:34
@knopp knopp added this pull request to the merge queue Dec 12, 2025
Merged via the queue into flutter:master with commit ca27d3c Dec 12, 2025
177 of 178 checks passed
@knopp knopp deleted the win_main_loop_deadlock branch December 12, 2025 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop engine flutter/engine related. See also e: labels. platform-windows Building on or for Windows specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows] Windows tasks aren't processed if the Dart event loop is never drained

2 participants