Repository navigation
Fix idle priority task during animation - #116422
AsturaPhoenix wants to merge 5 commits into
Conversation
8eddebc to
53ccc63
Compare
|
It looks like you may have run Please revert these formatting changes so it is easier to review your patch |
Adds a test exercising `scheduleTask` with `Priority.idle` while a `Ticker` is going off. This test demonstrates the issue by failing and is fixed by the next commit. Also necessitates cleaning up one of the previous tests to execute microtasks since warmup frames have an async, and simply running the exposed handler leaves dirty state.
Schedules tasks with priority < `Priority.animation` to possibly execute as a post-frame callback. Tasks with priority >= `Priority.animation` are executed at that time, and if there are no scheduled transient tasks awaiting the next frame then tasks with lower priority are executed as well. This has also been tested visually in an app.
The scheduler expects raw time to be monotonic, so avoid using time in the new test since it's not required.
53ccc63 to
4d9dc5a
Compare
|
@jonahwilliams Thanks; done. Also fixed the test failure under randomized ordering by avoiding incrementing time during the new test. There seems to be a complex interaction in |
This helps ensure that the queues are drained even if the test fails.
| } | ||
| return _taskQueue.isNotEmpty; | ||
| } else { | ||
| addPostFrameCallback((Duration timeStamp) { |
There was a problem hiding this comment.
Does this actually fix the problem of tasks not running, though? Adding a postframecallback does not force a frame. So, if currently no frame is running or scheduled, this may never run and tasks will never execute. I believe, there's no guarantee that a frame is running or scheduled at this point?
There was a problem hiding this comment.
It looks like a frame would be guaranteed under the default schedulingStrategy, which may only return false if there are transient frame callbacks, which implies a call to scheduleFrame.
I think if we'd want to relax this assumption to allow schedulingStrategys that could return false when there is not a frame scheduled, we'd need some notion of when to try again. If we were to define such a condition to be at the next vsync, the fix could be as simple as scheduling a frame here (and we'd want to add a note to the SchedulingStrategy documentation). Otherwise, if we don't define such a notion, I think we'd be forced to spin-poll as in #82781.
If we do decide this assumption is fine, we can add a note to SchedulingStrategy and assert(_hasScheduledFrame || !framesEnabled) here.
There was a problem hiding this comment.
For now I've just added a comment to SchedulingStrategy specifying the current behavior, that the strategy is re-evaluated after the next (or current) frame, or when a new task is scheduled. I haven't added any new assertions. I think this may be the least impactful way to go.
There was a problem hiding this comment.
It is odd to rely on the inner workings of the default scheduling strategy here, because that one could be fully replaced.
I don't have a great immediate idea of what to do here instead, though. Maybe @Hixie has some advice?
There was a problem hiding this comment.
A different way of looking at it with the documentation change is that rather than relying on the inner workings of the default scheduling strategy, we're defining that when a scheduling strategy returns false, tasks won't be re-evaluated again until "after the next (or current) frame, or until a new task is scheduled."
This shifts the implementation detail dependence so that it's contained within the expectations of the scheduling strategy itself, such that this patch would fix the expectation in the context of the default scheduling strategy. Were a scheduling strategy to be defined that returns false even if there are no frames scheduled, it would then not intrinsically have an expectation that the task/frame would be guaranteed to be scheduled, which I think is okay.
There was a problem hiding this comment.
Right, using addPostFrameCallback doesn't work in the case where, say, the scheduling strategy is "do tasks except on Tuesdays". On a Tuesday, the scheduler strategy would return false, but on Wednesday we wouldn't know to retrigger the task queue.
I wonder if anyone has ever used the scheduling strategy logic. If not, we could just remove it and hard-code our current strategy and then the assumption would be safe and addPostFrameCallback would work.
I don't think, with the current API, that adding addPostFrameCallback is a good solution because it prevents us from feeling the pain of the broken API (without making the API less broken).
Add a comment to `SchedulingStrategy` specifying that skipped tasks are re-evaluated after the next or current frame, or after a new task is scheduled.
|
There's another thing that warrants discussion. void _ensureEventLoopCallback() {
assert(!locked);
assert(_taskQueue.isNotEmpty);
if (_hasRequestedAnEventLoopCallback) {
return;
}
_hasRequestedAnEventLoopCallback = true;
Timer.run(_runTasks);
}
A full solution could be to make tasks cancellable and dispose the timer if all pending tasks are cancelled, but that doesn't seem worth the complexity. We can get around this by scheduling the event loop as a microtask, though this does effectively increase its priority somewhat. I don't think the effect should be otherwise noticeable. However, I'm inclined to just live with this. Thoughts? Example of what such a test needs to look like: await tester.pumpWidget(MyWidget());
await tester.pumpAndSettle();
await tester.pump(Duration.zero);
expect(find.byType(MyChild), findsOneWidget);Example of a test that to catch a callback that runs after the widget has been disposed: await tester.pumpWidget(MyWidget());
await tester.pumpAndSettle();
// dispose
await tester.pumpWidget(const SizedBox());
// make sure the widget doesn't attempt to setState after disposal
await tester.pump(Duration.zero); |
|
(Triage) what is the status of this PR? It looks like there has not been an update in a while here, @AsturaPhoenix is this waiting on another review? |
|
@Piinks Yes; @goderbauer raised a concern that I think stems from the limitations of the API. Please be advised that although I have this issue patched on my fork, my project is no longer hitting this issue.I was using this to deal with it being expensive to change z-ordering of a large number of markers on Google Maps, but this is no longer an issue now that we've migrated to |
|
I'm inclined to close this PR since we no longer require this behavior. |
This PR is based on #82781 but takes a slightly different approach where instead of polling the task queue on the
Timerqueue, it waits until after the post-frame callback so that it doesn't compete with animation tasks scheduled viascheduleFrameCallback.This also attempts to address the comments on the test by mimicking the calls at work and cleaning up earlier tests rather than using a real animator, at the expense of possible fidelity. An integration test may be warranted.
BASK.Trip.Planner.-.Google.Chrome.2022-12-02.06-07-46.mp4
This video shows this patch at work in https://github.com/AsturaPhoenix/trip_planner_aquamarine / http://34.83.198.158/trip_planner_aquamarine. Here, the marker z-ordering is changed depending on which tab is active, and the change is an expensive operation that is scheduled with
Priority.animation - 1.Issues: #82016
Pre-launch Checklist
///).