@@ -1649,6 +1649,102 @@ TEST_F(EmbedderTest, CanRemoveView) {
16491649 ASSERT_EQ (message, " View IDs: [0]" );
16501650}
16511651
1652+ // Regression test for:
1653+ // https://github.com/flutter/flutter/issues/164564
1654+ TEST_F (EmbedderTest, RemoveViewCallbackIsInvokedAfterRasterThreadIsDone) {
1655+ auto & context = GetEmbedderContext<EmbedderTestContextSoftware>();
1656+ EmbedderConfigBuilder builder (context);
1657+ std::mutex engine_mutex;
1658+ UniqueEngine engine;
1659+ auto render_thread = CreateNewThread (" custom_render_thread" );
1660+ EmbedderTestTaskRunner render_task_runner (
1661+ render_thread, [&](FlutterTask task) {
1662+ std::scoped_lock engine_lock (engine_mutex);
1663+ if (engine.is_valid ()) {
1664+ ASSERT_EQ (FlutterEngineRunTask (engine.get (), &task), kSuccess );
1665+ }
1666+ });
1667+
1668+ builder.SetSurface (SkISize::Make (1 , 1 ));
1669+ builder.SetDartEntrypoint (" remove_view_callback_too_early" );
1670+ builder.SetRenderTaskRunner (
1671+ &render_task_runner.GetFlutterTaskRunnerDescription ());
1672+
1673+ fml::AutoResetWaitableEvent ready_latch;
1674+ context.AddNativeCallback (
1675+ " SignalNativeTest" ,
1676+ CREATE_NATIVE_ENTRY (
1677+ [&ready_latch](Dart_NativeArguments args) { ready_latch.Signal (); }));
1678+
1679+ {
1680+ std::scoped_lock lock (engine_mutex);
1681+ engine = builder.InitializeEngine ();
1682+ }
1683+ ASSERT_EQ (FlutterEngineRunInitialized (engine.get ()), kSuccess );
1684+ ASSERT_TRUE (engine.is_valid ());
1685+
1686+ ready_latch.Wait ();
1687+
1688+ fml::AutoResetWaitableEvent add_view_latch;
1689+ // Add view 123.
1690+ FlutterWindowMetricsEvent metrics = {};
1691+ metrics.struct_size = sizeof (FlutterWindowMetricsEvent);
1692+ metrics.width = 800 ;
1693+ metrics.height = 600 ;
1694+ metrics.pixel_ratio = 1.0 ;
1695+ metrics.view_id = 123 ;
1696+
1697+ FlutterAddViewInfo add_info = {};
1698+ add_info.struct_size = sizeof (FlutterAddViewInfo);
1699+ add_info.view_id = 123 ;
1700+ add_info.view_metrics = &metrics;
1701+ add_info.user_data = &add_view_latch;
1702+ add_info.add_view_callback = [](const FlutterAddViewResult* result) {
1703+ ASSERT_TRUE (result->added );
1704+ auto add_view_latch =
1705+ reinterpret_cast <fml::AutoResetWaitableEvent*>(result->user_data );
1706+ add_view_latch->Signal ();
1707+ };
1708+ ASSERT_EQ (FlutterEngineAddView (engine.get (), &add_info), kSuccess );
1709+ add_view_latch.Wait ();
1710+
1711+ std::atomic_bool view_available = true ;
1712+
1713+ // Simulate pending rasterization task scheduled before view removal request
1714+ // that accesses view resources.
1715+ fml::AutoResetWaitableEvent raster_thread_latch;
1716+ render_thread->PostTask ([&] {
1717+ std::this_thread::sleep_for (std::chrono::milliseconds (50 ));
1718+ // View must be available.
1719+ EXPECT_TRUE (view_available);
1720+ raster_thread_latch.Signal ();
1721+ });
1722+
1723+ fml::AutoResetWaitableEvent remove_view_latch;
1724+ FlutterRemoveViewInfo remove_view_info = {};
1725+ remove_view_info.struct_size = sizeof (FlutterRemoveViewInfo);
1726+ remove_view_info.view_id = 123 ;
1727+ remove_view_info.user_data = &remove_view_latch;
1728+ remove_view_info.remove_view_callback =
1729+ [](const FlutterRemoveViewResult* result) {
1730+ ASSERT_TRUE (result->removed );
1731+ auto remove_view_latch =
1732+ reinterpret_cast <fml::AutoResetWaitableEvent*>(result->user_data );
1733+ remove_view_latch->Signal ();
1734+ };
1735+
1736+ // Remove the view and wait until the callback is called.
1737+ ASSERT_EQ (FlutterEngineRemoveView (engine.get (), &remove_view_info), kSuccess );
1738+ remove_view_latch.Wait ();
1739+
1740+ // After FlutterEngineRemoveViewCallback is called it should be safe to
1741+ // remove view - raster thread must not be accessing any view resources.
1742+ view_available = false ;
1743+ raster_thread_latch.Wait ();
1744+
1745+ FlutterEngineDeinitialize (engine.get ());
1746+ }
1747+
16521748// ------------------------------------------------------------------------------
16531749// / The implicit view is a special view that the engine and framework assume
16541750// / can *always* be rendered to. Test that this view cannot be removed.
0 commit comments