Skip to content

Commit 0ca0ef6

Browse files
authored
[Windows] Fix EGL surface destruction race (flutter#51781)
This fixes the `WindowsTest.EngineCanTransitionToHeadless` flakiness reported by @matanlurey. EGL surfaces can only be used by a single thread at a time. Concurrent operations are unsafe. Previously, the EGL surface was destroyed on the platform thread. This was safe as this always happened after the engine was shutdown and the raster thread was stopped. However, in a multi-view world a view can be destroyed while the engine is running. There may be pending raster tasks that operate on the render surface. Thus, the EGL surfaces should be destroyed on the raster thread when it is available. This bug was introduced by flutter/engine#51681 Part of flutter#142845 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 8ec35b6 commit 0ca0ef6

3 files changed

Lines changed: 66 additions & 10 deletions

File tree

shell/platform/windows/flutter_windows_view.cc

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#include <chrono>
88

99
#include "flutter/common/constants.h"
10+
#include "flutter/fml/make_copyable.h"
1011
#include "flutter/fml/platform/win/wstring_conversion.h"
12+
#include "flutter/fml/synchronization/waitable_event.h"
1113
#include "flutter/shell/platform/common/accessibility_bridge.h"
1214
#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h"
1315
#include "flutter/shell/platform/windows/text_input_plugin.h"
@@ -81,6 +83,22 @@ void UpdateVsync(const FlutterWindowsEngine& engine,
8183
}
8284
}
8385

86+
/// Destroys a rendering surface that backs a Flutter view.
87+
void DestroyWindowSurface(const FlutterWindowsEngine& engine,
88+
std::unique_ptr<egl::WindowSurface> surface) {
89+
// EGL surfaces are used on the raster thread if the engine is running.
90+
// There may be pending raster tasks that use this surface. Destroy the
91+
// surface on the raster thread to avoid concurrent uses.
92+
if (engine.running()) {
93+
engine.PostRasterThreadTask(fml::MakeCopyable(
94+
[surface = std::move(surface)] { surface->Destroy(); }));
95+
} else {
96+
// There's no raster thread if engine isn't running. The surface can be
97+
// destroyed on the platform thread.
98+
surface->Destroy();
99+
}
100+
}
101+
84102
} // namespace
85103

86104
FlutterWindowsView::FlutterWindowsView(
@@ -105,7 +123,9 @@ FlutterWindowsView::~FlutterWindowsView() {
105123
// Notify the engine the view's child window will no longer be visible.
106124
engine_->OnWindowStateEvent(GetWindowHandle(), WindowStateEvent::kHide);
107125

108-
DestroyRenderSurface();
126+
if (surface_) {
127+
DestroyWindowSurface(*engine_, std::move(surface_));
128+
}
109129
}
110130

111131
bool FlutterWindowsView::OnEmptyFrameGenerated() {
@@ -706,12 +726,6 @@ bool FlutterWindowsView::ResizeRenderSurface(size_t width, size_t height) {
706726
return true;
707727
}
708728

709-
void FlutterWindowsView::DestroyRenderSurface() {
710-
if (surface_) {
711-
surface_->Destroy();
712-
}
713-
}
714-
715729
egl::WindowSurface* FlutterWindowsView::surface() const {
716730
return surface_.get();
717731
}

shell/platform/windows/flutter_windows_view.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate {
5050
// Should be called before calling FlutterEngineRun using this view.
5151
void CreateRenderSurface();
5252

53-
// Destroys current rendering surface if one has been allocated.
54-
void DestroyRenderSurface();
55-
5653
// Get the EGL surface that backs the Flutter view.
5754
//
5855
// This might be nullptr or an invalid surface.

shell/platform/windows/flutter_windows_view_unittests.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ TEST(FlutterWindowsViewTest, Shutdown) {
264264
InSequence s;
265265
EXPECT_CALL(*engine_ptr, running).WillOnce(Return(true));
266266
EXPECT_CALL(*engine_ptr, RemoveView(view_id)).Times(1);
267+
EXPECT_CALL(*engine_ptr, running).WillOnce(Return(true));
268+
EXPECT_CALL(*engine_ptr, PostRasterThreadTask)
269+
.WillOnce([](fml::closure callback) {
270+
callback();
271+
return true;
272+
});
267273
EXPECT_CALL(*surface_ptr, Destroy).Times(1);
268274
}
269275
}
@@ -825,7 +831,14 @@ TEST(FlutterWindowsViewTest, WindowResizeTests) {
825831
auto windows_proc_table = std::make_shared<NiceMock<MockWindowsProcTable>>();
826832
std::unique_ptr<FlutterWindowsEngine> engine =
827833
GetTestEngine(windows_proc_table);
834+
828835
EngineModifier engine_modifier{engine.get()};
836+
engine_modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
837+
PostRenderThreadTask,
838+
([](auto engine, VoidCallback callback, void* user_data) {
839+
callback(user_data);
840+
return kSuccess;
841+
}));
829842

830843
auto egl_manager = std::make_unique<egl::MockManager>();
831844
auto surface = std::make_unique<egl::MockWindowSurface>();
@@ -883,7 +896,14 @@ TEST(FlutterWindowsViewTest, TestEmptyFrameResizes) {
883896
auto windows_proc_table = std::make_shared<NiceMock<MockWindowsProcTable>>();
884897
std::unique_ptr<FlutterWindowsEngine> engine =
885898
GetTestEngine(windows_proc_table);
899+
886900
EngineModifier engine_modifier{engine.get()};
901+
engine_modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
902+
PostRenderThreadTask,
903+
([](auto engine, VoidCallback callback, void* user_data) {
904+
callback(user_data);
905+
return kSuccess;
906+
}));
887907

888908
auto egl_manager = std::make_unique<egl::MockManager>();
889909
auto surface = std::make_unique<egl::MockWindowSurface>();
@@ -940,7 +960,14 @@ TEST(FlutterWindowsViewTest, TestEmptyFrameResizes) {
940960
// https://github.com/flutter/flutter/issues/141855
941961
TEST(FlutterWindowsViewTest, WindowResizeRace) {
942962
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
963+
943964
EngineModifier engine_modifier(engine.get());
965+
engine_modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
966+
PostRenderThreadTask,
967+
([](auto engine, VoidCallback callback, void* user_data) {
968+
callback(user_data);
969+
return kSuccess;
970+
}));
944971

945972
auto egl_manager = std::make_unique<egl::MockManager>();
946973
auto surface = std::make_unique<egl::MockWindowSurface>();
@@ -980,7 +1007,14 @@ TEST(FlutterWindowsViewTest, WindowResizeRace) {
9801007
// even though EGL initialized successfully.
9811008
TEST(FlutterWindowsViewTest, WindowResizeInvalidSurface) {
9821009
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1010+
9831011
EngineModifier engine_modifier(engine.get());
1012+
engine_modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
1013+
PostRenderThreadTask,
1014+
([](auto engine, VoidCallback callback, void* user_data) {
1015+
callback(user_data);
1016+
return kSuccess;
1017+
}));
9841018

9851019
auto egl_manager = std::make_unique<egl::MockManager>();
9861020
auto surface = std::make_unique<egl::MockWindowSurface>();
@@ -1477,6 +1511,11 @@ TEST(FlutterWindowsViewTest, DisablesVSyncAfterStartup) {
14771511
EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
14781512
EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true));
14791513
EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1514+
EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1515+
.WillOnce([](fml::closure callback) {
1516+
callback();
1517+
return true;
1518+
});
14801519
EXPECT_CALL(*surface_ptr, Destroy).Times(1);
14811520

14821521
EngineModifier modifier{engine.get()};
@@ -1519,6 +1558,12 @@ TEST(FlutterWindowsViewTest, EnablesVSyncAfterStartup) {
15191558
EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
15201559
EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true));
15211560
EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1561+
1562+
EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1563+
.WillOnce([](fml::closure callback) {
1564+
callback();
1565+
return true;
1566+
});
15221567
EXPECT_CALL(*surface_ptr, Destroy).Times(1);
15231568

15241569
EngineModifier modifier{engine.get()};

0 commit comments

Comments
 (0)