Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 91f250b

Browse files
author
Jonah Williams
authored
[engine] reland: more consistently flush message loops tasks (#56815)
Changes the following shell callbacks to flush the dart event loop: * OnPlatformViewSetViewportMetrics * OnPlatformViewDispatchPointerDataPacket * OnPlatformViewDispatchPlatformMessage * OnPlatformViewSetSemanticsEnabled * OnPlatformViewSetAccessibilityFeatures Using a new TaskRunner API RunNowAndFlushMessages. If the task runner can run tasks on the current thread, this will immediately invoke a callback and then post an empty task to the event loop to ensure dart listeners fire. Unlike #56738, does not touch the vsync API - which looks like it depends on scheduling behavior today, at least for iOS.
1 parent 65c8c6e commit 91f250b

File tree

6 files changed

+95
-36
lines changed

6 files changed

+95
-36
lines changed

fml/task_runner.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ bool TaskRunner::RunsTasksOnCurrentThread() {
5252
loop_queue_id);
5353
}
5454

55+
// static
5556
void TaskRunner::RunNowOrPostTask(const fml::RefPtr<fml::TaskRunner>& runner,
5657
const fml::closure& task) {
5758
FML_DCHECK(runner);
@@ -62,4 +63,20 @@ void TaskRunner::RunNowOrPostTask(const fml::RefPtr<fml::TaskRunner>& runner,
6263
}
6364
}
6465

66+
// static
67+
void TaskRunner::RunNowAndFlushMessages(
68+
const fml::RefPtr<fml::TaskRunner>& runner,
69+
const fml::closure& task) {
70+
FML_DCHECK(runner);
71+
if (runner->RunsTasksOnCurrentThread()) {
72+
task();
73+
// Post an empty task to make the UI message loop run its task observers.
74+
// The observers will execute any Dart microtasks queued by the platform
75+
// message handler.
76+
runner->PostTask([] {});
77+
} else {
78+
runner->PostTask(task);
79+
}
80+
}
81+
6582
} // namespace fml

fml/task_runner.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ class TaskRunner : public fml::RefCountedThreadSafe<TaskRunner>,
6262
static void RunNowOrPostTask(const fml::RefPtr<fml::TaskRunner>& runner,
6363
const fml::closure& task);
6464

65+
/// Like RunNowOrPostTask, except that if the task can be immediately
66+
/// executed, an empty task will still be posted to the runner afterwards.
67+
///
68+
/// This is used to ensure that messages posted to Dart from the platform
69+
/// thread always flush the Dart event loop.
70+
static void RunNowAndFlushMessages(const fml::RefPtr<fml::TaskRunner>& runner,
71+
const fml::closure& task);
72+
6573
protected:
6674
explicit TaskRunner(fml::RefPtr<MessageLoopImpl> loop);
6775

shell/common/fixtures/shell_test.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,3 +640,13 @@ void testSemanticsActions() {
640640
});
641641
};
642642
}
643+
644+
@pragma('vm:entry-point')
645+
void testPointerActions() {
646+
PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket pointer) async {
647+
await null;
648+
Future<void>.value().then((_) {
649+
notifyNative();
650+
});
651+
};
652+
}

shell/common/shell.cc

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#include "fml/task_runner.h"
56
#define RAPIDJSON_HAS_STDSTRING 1
67
#include "flutter/shell/common/shell.h"
78

@@ -1036,7 +1037,7 @@ void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id,
10361037
}
10371038
});
10381039

1039-
fml::TaskRunner::RunNowOrPostTask(
1040+
fml::TaskRunner::RunNowAndFlushMessages(
10401041
task_runners_.GetUITaskRunner(),
10411042
[engine = engine_->GetWeakPtr(), view_id, metrics]() {
10421043
if (engine) {
@@ -1075,24 +1076,16 @@ void Shell::OnPlatformViewDispatchPlatformMessage(
10751076
}
10761077
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
10771078

1078-
if (task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()) {
1079-
engine_->DispatchPlatformMessage(std::move(message));
1080-
1081-
// Post an empty task to make the UI message loop run its task observers.
1082-
// The observers will execute any Dart microtasks queued by the platform
1083-
// message handler.
1084-
task_runners_.GetUITaskRunner()->PostTask([] {});
1085-
} else {
1086-
// The static leak checker gets confused by the use of fml::MakeCopyable.
1087-
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1088-
task_runners_.GetUITaskRunner()->PostTask(
1089-
fml::MakeCopyable([engine = engine_->GetWeakPtr(),
1090-
message = std::move(message)]() mutable {
1091-
if (engine) {
1092-
engine->DispatchPlatformMessage(std::move(message));
1093-
}
1094-
}));
1095-
}
1079+
// The static leak checker gets confused by the use of fml::MakeCopyable.
1080+
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1081+
fml::TaskRunner::RunNowAndFlushMessages(
1082+
task_runners_.GetUITaskRunner(),
1083+
fml::MakeCopyable([engine = engine_->GetWeakPtr(),
1084+
message = std::move(message)]() mutable {
1085+
if (engine) {
1086+
engine->DispatchPlatformMessage(std::move(message));
1087+
}
1088+
}));
10961089
}
10971090

10981091
// |PlatformView::Delegate|
@@ -1104,7 +1097,7 @@ void Shell::OnPlatformViewDispatchPointerDataPacket(
11041097
TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_);
11051098
FML_DCHECK(is_set_up_);
11061099
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1107-
fml::TaskRunner::RunNowOrPostTask(
1100+
fml::TaskRunner::RunNowAndFlushMessages(
11081101
task_runners_.GetUITaskRunner(),
11091102
fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet),
11101103
flow_id = next_pointer_flow_id_]() mutable {
@@ -1122,7 +1115,8 @@ void Shell::OnPlatformViewDispatchSemanticsAction(int32_t node_id,
11221115
FML_DCHECK(is_set_up_);
11231116
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
11241117

1125-
task_runners_.GetUITaskRunner()->PostTask(
1118+
fml::TaskRunner::RunNowAndFlushMessages(
1119+
task_runners_.GetUITaskRunner(),
11261120
fml::MakeCopyable([engine = engine_->GetWeakPtr(), node_id, action,
11271121
args = std::move(args)]() mutable {
11281122
if (engine) {
@@ -1136,25 +1130,26 @@ void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) {
11361130
FML_DCHECK(is_set_up_);
11371131
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
11381132

1139-
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
1140-
[engine = engine_->GetWeakPtr(), enabled] {
1141-
if (engine) {
1142-
engine->SetSemanticsEnabled(enabled);
1143-
}
1144-
});
1133+
fml::TaskRunner::RunNowAndFlushMessages(
1134+
task_runners_.GetUITaskRunner(),
1135+
[engine = engine_->GetWeakPtr(), enabled] {
1136+
if (engine) {
1137+
engine->SetSemanticsEnabled(enabled);
1138+
}
1139+
});
11451140
}
11461141

11471142
// |PlatformView::Delegate|
11481143
void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) {
11491144
FML_DCHECK(is_set_up_);
11501145
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
11511146

1152-
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
1153-
[engine = engine_->GetWeakPtr(), flags] {
1154-
if (engine) {
1155-
engine->SetAccessibilityFeatures(flags);
1156-
}
1157-
});
1147+
fml::TaskRunner::RunNowAndFlushMessages(
1148+
task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), flags] {
1149+
if (engine) {
1150+
engine->SetAccessibilityFeatures(flags);
1151+
}
1152+
});
11581153
}
11591154

11601155
// |PlatformView::Delegate|

shell/common/shell_unittests.cc

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4312,7 +4312,8 @@ TEST_F(ShellTest, NavigationMessageDispachedImmediately) {
43124312
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
43134313
}
43144314

4315-
TEST_F(ShellTest, SemanticsActionsPostTask) {
4315+
// Verifies a semantics Action will flush the dart event loop.
4316+
TEST_F(ShellTest, SemanticsActionsFlushMessageLoop) {
43164317
Settings settings = CreateSettingsForFixture();
43174318
ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
43184319
ThreadHost::Type::kPlatform);
@@ -4327,20 +4328,49 @@ TEST_F(ShellTest, SemanticsActionsPostTask) {
43274328
configuration.SetEntrypoint("testSemanticsActions");
43284329

43294330
RunEngine(shell.get(), std::move(configuration));
4331+
fml::CountDownLatch latch(1);
4332+
AddNativeCallback(
4333+
// The Dart native function names aren't very consistent but this is
4334+
// just the native function name of the second vm entrypoint in the
4335+
// fixture.
4336+
"NotifyNative",
4337+
CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
43304338

43314339
task_runners.GetPlatformTaskRunner()->PostTask([&] {
43324340
SendSemanticsAction(shell.get(), 0, SemanticsAction::kTap,
43334341
fml::MallocMapping(nullptr, 0));
43344342
});
4343+
latch.Wait();
43354344

4336-
// Fulfill native function for the second Shell's entrypoint.
4345+
DestroyShell(std::move(shell), task_runners);
4346+
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
4347+
}
4348+
4349+
// Verifies a pointer event will flush the dart event loop.
4350+
TEST_F(ShellTest, PointerPacketFlushMessageLoop) {
4351+
Settings settings = CreateSettingsForFixture();
4352+
ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
4353+
ThreadHost::Type::kPlatform);
4354+
auto task_runner = thread_host.platform_thread->GetTaskRunner();
4355+
TaskRunners task_runners("test", task_runner, task_runner, task_runner,
4356+
task_runner);
4357+
4358+
EXPECT_EQ(task_runners.GetPlatformTaskRunner(),
4359+
task_runners.GetUITaskRunner());
4360+
auto shell = CreateShell(settings, task_runners);
4361+
auto configuration = RunConfiguration::InferFromSettings(settings);
4362+
configuration.SetEntrypoint("testPointerActions");
4363+
4364+
RunEngine(shell.get(), std::move(configuration));
43374365
fml::CountDownLatch latch(1);
43384366
AddNativeCallback(
43394367
// The Dart native function names aren't very consistent but this is
43404368
// just the native function name of the second vm entrypoint in the
43414369
// fixture.
43424370
"NotifyNative",
43434371
CREATE_NATIVE_ENTRY([&](auto args) { latch.CountDown(); }));
4372+
4373+
DispatchFakePointerData(shell.get(), 23);
43444374
latch.Wait();
43454375

43464376
DestroyShell(std::move(shell), task_runners);

shell/common/vsync_waiter.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
127127

128128
fml::TaskQueueId ui_task_queue_id =
129129
task_runners_.GetUITaskRunner()->GetTaskQueueId();
130-
131130
task_runners_.GetUITaskRunner()->PostTask(
132131
[ui_task_queue_id, callback, flow_identifier, frame_start_time,
133132
frame_target_time, pause_secondary_tasks]() {

0 commit comments

Comments
 (0)