Skip to content

Commit faf44fe

Browse files
Improve C++ plugin lifetime handling (flutter#17489)
This makes two changes: - Adds a way to register a callback for when a FlutterDesktopPluginRegistrarRef is destroyed, and implements the logic to call it in the Windows and Linux embeddings. - Adds a class to the C++ wrapper that handles making a singleton owning PluginRegistrar wrappers, and destroying them when the underlying reference goes away, to avoid needing that boilerplate code in every plugin's source. Fixes flutter#53496
1 parent 0945635 commit faf44fe

File tree

11 files changed

+175
-10
lines changed

11 files changed

+175
-10
lines changed

shell/platform/common/cpp/client_wrapper/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,6 @@ executable("client_wrapper_unittests") {
6565
# https://github.com/flutter/flutter/issues/41414.
6666
"//third_party/dart/runtime:libdart_jit",
6767
]
68+
69+
defines = [ "FLUTTER_DESKTOP_LIBRARY" ]
6870
}

shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_
66
#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_
77

8+
#include <map>
89
#include <memory>
910
#include <set>
1011
#include <string>
@@ -69,6 +70,48 @@ class Plugin {
6970
virtual ~Plugin() = default;
7071
};
7172

73+
// A singleton to own PluginRegistrars. This is intended for use in plugins,
74+
// where there is no higher-level object to own a PluginRegistrar that can
75+
// own plugin instances and ensure that they live as long as the engine they
76+
// are registered with.
77+
class PluginRegistrarManager {
78+
public:
79+
static PluginRegistrarManager* GetInstance();
80+
81+
// Prevent copying.
82+
PluginRegistrarManager(PluginRegistrarManager const&) = delete;
83+
PluginRegistrarManager& operator=(PluginRegistrarManager const&) = delete;
84+
85+
// Returns a plugin registrar wrapper of type T, which must be a kind of
86+
// PluginRegistrar, creating it if necessary. The returned registrar will
87+
// live as long as the underlying FlutterDesktopPluginRegistrarRef, so
88+
// can be used to own plugin instances.
89+
//
90+
// Calling this multiple times for the same registrar_ref with different
91+
// template types results in undefined behavior.
92+
template <class T>
93+
T* GetRegistrar(FlutterDesktopPluginRegistrarRef registrar_ref) {
94+
auto insert_result =
95+
registrars_.emplace(registrar_ref, std::make_unique<T>(registrar_ref));
96+
auto& registrar_pair = *(insert_result.first);
97+
FlutterDesktopRegistrarSetDestructionHandler(registrar_pair.first,
98+
OnRegistrarDestroyed);
99+
return static_cast<T*>(registrar_pair.second.get());
100+
}
101+
102+
private:
103+
PluginRegistrarManager();
104+
105+
using WrapperMap = std::map<FlutterDesktopPluginRegistrarRef,
106+
std::unique_ptr<PluginRegistrar>>;
107+
108+
static void OnRegistrarDestroyed(FlutterDesktopPluginRegistrarRef registrar);
109+
110+
WrapperMap* registrars() { return &registrars_; }
111+
112+
WrapperMap registrars_;
113+
};
114+
72115
} // namespace flutter
73116

74117
#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_

shell/platform/common/cpp/client_wrapper/plugin_registrar.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ void BinaryMessengerImpl::SetMessageHandler(const std::string& channel,
138138
ForwardToHandler, message_handler);
139139
}
140140

141-
// PluginRegistrar:
141+
// ===== PluginRegistrar =====
142142

143143
PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar)
144144
: registrar_(registrar) {
@@ -157,4 +157,20 @@ void PluginRegistrar::EnableInputBlockingForChannel(
157157
FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str());
158158
}
159159

160+
// ===== PluginRegistrarManager =====
161+
162+
// static
163+
PluginRegistrarManager* PluginRegistrarManager::GetInstance() {
164+
static PluginRegistrarManager* instance = new PluginRegistrarManager();
165+
return instance;
166+
}
167+
168+
PluginRegistrarManager::PluginRegistrarManager() = default;
169+
170+
// static
171+
void PluginRegistrarManager::OnRegistrarDestroyed(
172+
FlutterDesktopPluginRegistrarRef registrar) {
173+
GetInstance()->registrars()->erase(registrar);
174+
}
175+
160176
} // namespace flutter

shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,36 @@ class TestApi : public testing::StubFlutterApi {
3333
return message_engine_result;
3434
}
3535

36-
// Called for FlutterDesktopMessengerSetCallback.
3736
void MessengerSetCallback(const char* channel,
3837
FlutterDesktopMessageCallback callback,
3938
void* user_data) override {
40-
last_callback_set_ = callback;
39+
last_message_callback_set_ = callback;
40+
}
41+
42+
void RegistrarSetDestructionHandler(
43+
FlutterDesktopOnRegistrarDestroyed callback) override {
44+
last_destruction_callback_set_ = callback;
4145
}
4246

4347
const uint8_t* last_data_sent() { return last_data_sent_; }
44-
FlutterDesktopMessageCallback last_callback_set() {
45-
return last_callback_set_;
48+
FlutterDesktopMessageCallback last_message_callback_set() {
49+
return last_message_callback_set_;
50+
}
51+
FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set() {
52+
return last_destruction_callback_set_;
4653
}
4754

4855
private:
4956
const uint8_t* last_data_sent_ = nullptr;
50-
FlutterDesktopMessageCallback last_callback_set_ = nullptr;
57+
FlutterDesktopMessageCallback last_message_callback_set_ = nullptr;
58+
FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set_ = nullptr;
5159
};
5260

5361
} // namespace
5462

5563
// Tests that the registrar returns a messenger that passes Send through to the
5664
// C API.
57-
TEST(MethodCallTest, MessengerSend) {
65+
TEST(PluginRegistrarTest, MessengerSend) {
5866
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
5967
auto test_api = static_cast<TestApi*>(scoped_api_stub.stub());
6068

@@ -70,7 +78,7 @@ TEST(MethodCallTest, MessengerSend) {
7078

7179
// Tests that the registrar returns a messenger that passes callback
7280
// registration and unregistration through to the C API.
73-
TEST(MethodCallTest, MessengerSetMessageHandler) {
81+
TEST(PluginRegistrarTest, MessengerSetMessageHandler) {
7482
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
7583
auto test_api = static_cast<TestApi*>(scoped_api_stub.stub());
7684

@@ -85,11 +93,60 @@ TEST(MethodCallTest, MessengerSetMessageHandler) {
8593
const size_t message_size,
8694
BinaryReply reply) {};
8795
messenger->SetMessageHandler(channel_name, std::move(binary_handler));
88-
EXPECT_NE(test_api->last_callback_set(), nullptr);
96+
EXPECT_NE(test_api->last_message_callback_set(), nullptr);
8997

9098
// Unregister.
9199
messenger->SetMessageHandler(channel_name, nullptr);
92-
EXPECT_EQ(test_api->last_callback_set(), nullptr);
100+
EXPECT_EQ(test_api->last_message_callback_set(), nullptr);
101+
}
102+
103+
// Tests that the registrar manager returns the same instance when getting
104+
// the wrapper for the same reference.
105+
TEST(PluginRegistrarTest, ManagerSameInstance) {
106+
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
107+
108+
auto dummy_registrar_handle =
109+
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
110+
111+
PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance();
112+
EXPECT_EQ(manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle),
113+
manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle));
114+
}
115+
116+
// Tests that the registrar manager returns different objects for different
117+
// references.
118+
TEST(PluginRegistrarTest, ManagerDifferentInstances) {
119+
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
120+
121+
auto dummy_registrar_handle_a =
122+
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
123+
auto dummy_registrar_handle_b =
124+
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(2);
125+
126+
PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance();
127+
EXPECT_NE(manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle_a),
128+
manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle_b));
129+
}
130+
131+
// Tests that the registrar manager deletes wrappers when the underlying
132+
// reference is destroyed.
133+
TEST(PluginRegistrarTest, ManagerRemovesOnDestruction) {
134+
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
135+
auto test_api = static_cast<TestApi*>(scoped_api_stub.stub());
136+
137+
auto dummy_registrar_handle =
138+
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
139+
PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance();
140+
auto* first_wrapper =
141+
manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle);
142+
143+
// Simulate destruction of the reference.
144+
EXPECT_NE(test_api->last_destruction_callback_set(), nullptr);
145+
test_api->last_destruction_callback_set()(dummy_registrar_handle);
146+
147+
// Requesting the wrapper should now create a new object.
148+
EXPECT_NE(manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle),
149+
first_wrapper);
93150
}
94151

95152
} // namespace flutter

shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
4444
return reinterpret_cast<FlutterDesktopMessengerRef>(1);
4545
}
4646

47+
void FlutterDesktopRegistrarSetDestructionHandler(
48+
FlutterDesktopPluginRegistrarRef registrar,
49+
FlutterDesktopOnRegistrarDestroyed callback) {
50+
if (s_stub_implementation) {
51+
s_stub_implementation->RegistrarSetDestructionHandler(callback);
52+
}
53+
}
54+
4755
void FlutterDesktopRegistrarEnableInputBlocking(
4856
FlutterDesktopPluginRegistrarRef registrar,
4957
const char* channel) {

shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class StubFlutterApi {
3434

3535
virtual ~StubFlutterApi() {}
3636

37+
// Called for FlutterDesktopRegistrarSetDestructionHandler.
38+
virtual void RegistrarSetDestructionHandler(
39+
FlutterDesktopOnRegistrarDestroyed callback) {}
40+
3741
// Called for FlutterDesktopRegistrarEnableInputBlocking.
3842
virtual void RegistrarEnableInputBlocking(const char* channel) {}
3943

shell/platform/common/cpp/public/flutter_plugin_registrar.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,19 @@ extern "C" {
1818
// Opaque reference to a plugin registrar.
1919
typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef;
2020

21+
// Function pointer type for registrar destruction callback.
22+
typedef void (*FlutterDesktopOnRegistrarDestroyed)(
23+
FlutterDesktopPluginRegistrarRef);
24+
2125
// Returns the engine messenger associated with this registrar.
2226
FLUTTER_EXPORT FlutterDesktopMessengerRef
2327
FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar);
2428

29+
// Registers a callback to be called when the plugin registrar is destroyed.
30+
FLUTTER_EXPORT void FlutterDesktopRegistrarSetDestructionHandler(
31+
FlutterDesktopPluginRegistrarRef registrar,
32+
FlutterDesktopOnRegistrarDestroyed callback);
33+
2534
// Enables input blocking on the given channel.
2635
//
2736
// If set, then the Flutter window will disable input callbacks

shell/platform/glfw/flutter_glfw.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ struct FlutterDesktopPluginRegistrar {
118118

119119
// The handle for the window associated with this registrar.
120120
FlutterDesktopWindow* window;
121+
122+
// Callback to be called on registrar destruction.
123+
FlutterDesktopOnRegistrarDestroyed destruction_handler;
121124
};
122125

123126
// State associated with the messenger used to communicate with the engine.
@@ -682,6 +685,11 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow(
682685
}
683686

684687
void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) {
688+
FlutterDesktopPluginRegistrarRef registrar =
689+
controller->plugin_registrar.get();
690+
if (registrar->destruction_handler) {
691+
registrar->destruction_handler(registrar);
692+
}
685693
FlutterEngineShutdown(controller->engine);
686694
delete controller;
687695
}
@@ -832,6 +840,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
832840
return registrar->messenger.get();
833841
}
834842

843+
void FlutterDesktopRegistrarSetDestructionHandler(
844+
FlutterDesktopPluginRegistrarRef registrar,
845+
FlutterDesktopOnRegistrarDestroyed callback) {
846+
registrar->destruction_handler = callback;
847+
}
848+
835849
FlutterDesktopWindowRef FlutterDesktopRegistrarGetWindow(
836850
FlutterDesktopPluginRegistrarRef registrar) {
837851
return registrar->window;

shell/platform/windows/flutter_windows.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
248248
return registrar->messenger.get();
249249
}
250250

251+
void FlutterDesktopRegistrarSetDestructionHandler(
252+
FlutterDesktopPluginRegistrarRef registrar,
253+
FlutterDesktopOnRegistrarDestroyed callback) {
254+
registrar->destruction_handler = callback;
255+
}
256+
251257
FlutterDesktopViewRef FlutterDesktopRegistrarGetView(
252258
FlutterDesktopPluginRegistrarRef registrar) {
253259
return registrar->window;

shell/platform/windows/win32_flutter_window.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Win32FlutterWindow::Win32FlutterWindow(int width, int height) {
1515

1616
Win32FlutterWindow::~Win32FlutterWindow() {
1717
DestroyRenderSurface();
18+
if (plugin_registrar_ && plugin_registrar_->destruction_handler) {
19+
plugin_registrar_->destruction_handler(plugin_registrar_.get());
20+
}
1821
}
1922

2023
FlutterDesktopViewControllerRef Win32FlutterWindow::CreateWin32FlutterWindow(

0 commit comments

Comments
 (0)