From 72cc1d198c954f5748cd1881b948d68ebd2d2d26 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 19 Feb 2025 14:52:07 -0800 Subject: [PATCH] [iOS] always post new task during gesture dispatch. (#163646) Fixes https://github.com/flutter/flutter/issues/163429 If we synchronously call into dart ui when dispatching pointer events, then we only end up scheduling about ~half as many frames as we should. Nothing is blocked, no task starvation, et cetera. It should always be safe to post pointer events as a new task instead. --- .../shell/common/fixtures/shell_test.dart | 7 ++++ engine/src/flutter/shell/common/shell.cc | 4 +-- .../flutter/shell/common/shell_unittests.cc | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/shell/common/fixtures/shell_test.dart b/engine/src/flutter/shell/common/fixtures/shell_test.dart index c217114682..c4fd1f2cd3 100644 --- a/engine/src/flutter/shell/common/fixtures/shell_test.dart +++ b/engine/src/flutter/shell/common/fixtures/shell_test.dart @@ -625,3 +625,10 @@ void testPointerActions() { }); }; } + +@pragma('vm:entry-point') +void testDispatchEvents() { + PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket pointer) { + notifyNative(); + }; +} diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index e8c8f3e846..3c37ad0ac7 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1095,8 +1095,8 @@ void Shell::OnPlatformViewDispatchPointerDataPacket( TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - fml::TaskRunner::RunNowAndFlushMessages( - task_runners_.GetUITaskRunner(), + + task_runners_.GetUITaskRunner()->PostTask( fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet), flow_id = next_pointer_flow_id_]() mutable { if (engine) { diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index f6d9751bb4..c8f1c796cd 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -4376,6 +4376,42 @@ TEST_F(ShellTest, PointerPacketFlushMessageLoop) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); } +// Verifies a pointer event will flush the dart event loop. +TEST_F(ShellTest, PointerPacketsAreDispatchedWithTask) { + Settings settings = CreateSettingsForFixture(); + ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::kPlatform); + auto task_runner = thread_host.platform_thread->GetTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + + EXPECT_EQ(task_runners.GetPlatformTaskRunner(), + task_runners.GetUITaskRunner()); + auto shell = CreateShell(settings, task_runners); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("testDispatchEvents"); + + RunEngine(shell.get(), std::move(configuration)); + fml::CountDownLatch latch(1); + bool did_invoke_callback = false; + AddNativeCallback( + // The Dart native function names aren't very consistent but this is + // just the native function name of the second vm entrypoint in the + // fixture. + "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) { + did_invoke_callback = true; + latch.CountDown(); + })); + + DispatchFakePointerData(shell.get(), 23); + EXPECT_FALSE(did_invoke_callback); + latch.Wait(); + EXPECT_TRUE(did_invoke_callback); + + DestroyShell(std::move(shell), task_runners); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + TEST_F(ShellTest, DiesIfSoftwareRenderingAndImpellerAreEnabledDeathTest) { #if defined(OS_FUCHSIA) GTEST_SKIP() << "Fuchsia";