Skip to content

Service platform tasks due within a frame early - #29

Merged
lhoward merged 1 commit into
mainfrom
poll-due-tasks-within-frame
Sep 14, 2026
Merged

lhoward merged 1 commit into
mainfrom
poll-due-tasks-within-frame

Conversation

@lhoward

@lhoward lhoward commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Both FlutterWindow run loops (the Timer path and run() async) slept for at least a full frame period, even when processMessages() said the next engine task was due sooner. That delayed such tasks by up to one frame, about 16.7 ms at 60 Hz. The C++ reference runner in flutter-embedded-linux sleeps only wait_duration in that case.

This change wakes the loop when that task is due, with a 1 ms floor.

CPU impact should be negligible:

  • When the queue is empty, processMessages() returns Int64.max and both loops still wake once per frame, as before.
  • Extra wakeups happen only while a task is due within the next frame. The 1 ms floor caps the rate.
  • Each extra pass is cheap: a mutex-guarded queue check in TaskRunner::ProcessTasks(), then dispatchEvent(), which doesn't block (Wayland poll with timeout 0, XPending on X11, sd_event_run/uv_run with no wait on DRM).
  • On Wayland, dispatchEvent() also delivers vsync, but VsyncWaiter::NotifyVsync only fires if the engine requested a frame, and the frame start is still aligned to the next vsync boundary. Frames can't render faster than the refresh rate.

Timer path: the timer callback already sets fireDate to delay the next fire. CFRunLoop honours any fireDate set during the callout that's later than the fire that just happened, so setting an earlier one (now + max(wait, 1 ms)) works the same way.

The existing behaviour of sleeping longer than a frame when the next task is further out is unchanged.

Independent of #28 and merges cleanly with it. Built with swift build --target FlutterSwift; not yet tested on target hardware.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F1bgmVQVQTHUQpQtBbFw6t

Both run loops slept a full frame period whenever the next engine task was
due sooner, adding up to a frame of latency. Wake at the task's due time
instead, with a 1ms floor. Idle behaviour (empty queue) is unchanged, and
vsync is still aligned to frame boundaries by the embedder.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F1bgmVQVQTHUQpQtBbFw6t
@lhoward
lhoward merged commit 090a4ff into main Sep 14, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant