Repository navigation
Tasks scheduled through 'SchedulerBinding.instance.scheduleTask' were not executed #82016
Description
Issue
example :https://dartpad.dev/8944b568287269991e5b943accf16861?null_safety=true
I start the animation during initialization and then execute the task . After the animation ends, the task is not executed.
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 3000));
animation = Tween(begin: 1.0, end: 0.0).animate(animationController);
// start an animation
animationController.forward();
// These task will be blocked even if the animation ends
SchedulerBinding.instance!.scheduleTask(task1, Priority.idle);
SchedulerBinding.instance!.scheduleTask(task2, Priority.idle);
}Expected results:
Task1 and Task2 are executed after the animation ends
Actual results:
After the animation ends, the task is not executed.
My point of view:
I notice that task1 and task2 are added to _taskQueue for scheduling here
flutter/packages/flutter/lib/src/scheduler/binding.dart
Lines 427 to 441 in 3ab799b
| void _ensureEventLoopCallback() { | |
| assert(!locked); | |
| assert(_taskQueue.isNotEmpty); | |
| if (_hasRequestedAnEventLoopCallback) | |
| return; | |
| _hasRequestedAnEventLoopCallback = true; | |
| Timer.run(_runTasks); | |
| } | |
| // Scheduled by _ensureEventLoopCallback. | |
| void _runTasks() { | |
| _hasRequestedAnEventLoopCallback = false; | |
| if (handleEventLoopCallback()) | |
| _ensureEventLoopCallback(); // runs next task when there's time | |
| } |
The problem is that once the handleEventLoopCallback() returns false, the _TaskQueue will not be scheduled again until the next scheduleTask.
In this case, because the Priority of the two tasks is Priority.idle, the schedulingStrategy returns false
flutter/packages/flutter/lib/src/scheduler/binding.dart
Lines 455 to 463 in 3ab799b
| bool handleEventLoopCallback() { | |
| if (_taskQueue.isEmpty || locked) | |
| return false; | |
| final _TaskEntry<dynamic> entry = _taskQueue.first; | |
| if (schedulingStrategy(priority: entry.priority, scheduler: this)) { | |
| try { | |
| _taskQueue.removeFirst(); | |
| entry.run(); | |
| } catch (exception, exceptionStack) { |
So task1 and task2 are still blocked after the animation ends
I try to modify these method like this :
void _ensureEventLoopCallback() {
assert(!locked);
assert(_taskQueue.isNotEmpty);
if (_hasRequestedAnEventLoopCallback)
return;
_hasRequestedAnEventLoopCallback = true;
print('_ensureEventLoopCallback execute');
Timer.run(_runTasks);
}
// Scheduled by _ensureEventLoopCallback.
void _runTasks() {
_hasRequestedAnEventLoopCallback = false;
if (handleEventLoopCallback())
_ensureEventLoopCallback();
else{
// When the execution fails, determines if the queue is empty
if(_taskQueue.isNotEmpty){
// When the task queue is not empty, the judgment is made again
_ensureEventLoopCallback();
}
} // runs next task when there's time
}When the task queue is not empty, the judgment is made again, just like Event loop
In the above example, tasks 1 and 2 were successfully executed after the animation ended
However, through logging, I found that _ensureEventLoopCallback() was called too frequently. Because the Timer.run will continue to add tasks to the isolate while the _taskQueue is not empty
However, I don't think this is necessary, because the result of the schedulingStrategy will not change until the next frame is drawn
So i modify _ensureEventLoopCallback() like this:
// Ensures that the scheduler services a task scheduled by [scheduleTask].
void _ensureEventLoopCallback() async{
assert(!locked);
assert(_taskQueue.isNotEmpty);
if (_hasRequestedAnEventLoopCallback)
return;
_hasRequestedAnEventLoopCallback = true;
print('_ensureEventLoopCallback execute');
// Executes after each frame is rendered
await endOfFrame;
_runTasks();
}At the end of each frame, the _taskQueue is scheduled .
And tasks 1 and 2 were also successfully executed after the animation ended
What do you think, if this change is necessary, I can PR to fix it
[✓] Flutter (Channel stable, 2.0.6, on macOS 11.2.2 20D80 darwin-x64, locale zh-Hans-CN)
• Flutter version 2.0.6 at /Users/nayuta/Desktop/fsdk/flutter2.0
• Framework revision 1d9032c7e1 (7 days ago), 2021-04-29 17:37:58 -0700
• Engine revision 05e680e202
• Dart version 2.12.3
• Pub download mirror http://pub.ke.com
• Flutter download mirror https://storage.flutter-io.cn
Activity
darshankawar commentedon May 7, 2021
flutter doctor -v
Hixie commentedon May 7, 2021
cc @goderbauer
goderbauer commentedon May 10, 2021
It does look like a bug that the tasks do not run until another task is scheduled. Happy to look at a PR to fix the problem.
Nayuta403 commentedon May 11, 2021
OK I will fix it
exaby73 commentedon May 4, 2022
Tested on current stable and master, issue still persists (see attached video).
Code sample
Code example used (same as one provided in DartPad by OP)
Some more insights on this, I have tried scheduling another task using
scheduleTaskafter 5 seconds of delay, 2 seconds after the animation stops, switching between all availablePrioritys, but neither the first 2 nor the last one in theFuturewas executed. See the modified code.Modified code
Video
82016_stable_ios.mov
flutter doctor -vNayuta403 commentedon May 13, 2022
Yeah I plan to reopen PR this month @exaby73
AsturaPhoenix commentedon Dec 2, 2022
Here's another take on this PR, which services the tasks as a post-frame callback rather than on the timer queue, and attempts to address the testing concerns using zoned timers and cleaning up after one of the earlier tests:
#116422
Fix scheduler event loop being stuck due to task with Priority.idle (#…
danagbemava-nc commentedon Jul 3, 2024
Closing this as fixed in #151168
Fix scheduler event loop being stuck due to task with Priority.idle (f…
github-actions commentedon Jul 17, 2024
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.