未验证 提交 faf44fed 编写于 作者: S stuartmorgan 提交者: GitHub

Improve C++ plugin lifetime handling (#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 https://github.com/flutter/flutter/issues/53496
上级 09456356
......@@ -65,4 +65,6 @@ executable("client_wrapper_unittests") {
# https://github.com/flutter/flutter/issues/41414.
"//third_party/dart/runtime:libdart_jit",
]
defines = [ "FLUTTER_DESKTOP_LIBRARY" ]
}
......@@ -5,6 +5,7 @@
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_
#include <map>
#include <memory>
#include <set>
#include <string>
......@@ -69,6 +70,48 @@ class Plugin {
virtual ~Plugin() = default;
};
// A singleton to own PluginRegistrars. This is intended for use in plugins,
// where there is no higher-level object to own a PluginRegistrar that can
// own plugin instances and ensure that they live as long as the engine they
// are registered with.
class PluginRegistrarManager {
public:
static PluginRegistrarManager* GetInstance();
// Prevent copying.
PluginRegistrarManager(PluginRegistrarManager const&) = delete;
PluginRegistrarManager& operator=(PluginRegistrarManager const&) = delete;
// Returns a plugin registrar wrapper of type T, which must be a kind of
// PluginRegistrar, creating it if necessary. The returned registrar will
// live as long as the underlying FlutterDesktopPluginRegistrarRef, so
// can be used to own plugin instances.
//
// Calling this multiple times for the same registrar_ref with different
// template types results in undefined behavior.
template <class T>
T* GetRegistrar(FlutterDesktopPluginRegistrarRef registrar_ref) {
auto insert_result =
registrars_.emplace(registrar_ref, std::make_unique<T>(registrar_ref));
auto& registrar_pair = *(insert_result.first);
FlutterDesktopRegistrarSetDestructionHandler(registrar_pair.first,
OnRegistrarDestroyed);
return static_cast<T*>(registrar_pair.second.get());
}
private:
PluginRegistrarManager();
using WrapperMap = std::map<FlutterDesktopPluginRegistrarRef,
std::unique_ptr<PluginRegistrar>>;
static void OnRegistrarDestroyed(FlutterDesktopPluginRegistrarRef registrar);
WrapperMap* registrars() { return &registrars_; }
WrapperMap registrars_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_
......@@ -138,7 +138,7 @@ void BinaryMessengerImpl::SetMessageHandler(const std::string& channel,
ForwardToHandler, message_handler);
}
// PluginRegistrar:
// ===== PluginRegistrar =====
PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar)
: registrar_(registrar) {
......@@ -157,4 +157,20 @@ void PluginRegistrar::EnableInputBlockingForChannel(
FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str());
}
// ===== PluginRegistrarManager =====
// static
PluginRegistrarManager* PluginRegistrarManager::GetInstance() {
static PluginRegistrarManager* instance = new PluginRegistrarManager();
return instance;
}
PluginRegistrarManager::PluginRegistrarManager() = default;
// static
void PluginRegistrarManager::OnRegistrarDestroyed(
FlutterDesktopPluginRegistrarRef registrar) {
GetInstance()->registrars()->erase(registrar);
}
} // namespace flutter
......@@ -33,28 +33,36 @@ class TestApi : public testing::StubFlutterApi {
return message_engine_result;
}
// Called for FlutterDesktopMessengerSetCallback.
void MessengerSetCallback(const char* channel,
FlutterDesktopMessageCallback callback,
void* user_data) override {
last_callback_set_ = callback;
last_message_callback_set_ = callback;
}
void RegistrarSetDestructionHandler(
FlutterDesktopOnRegistrarDestroyed callback) override {
last_destruction_callback_set_ = callback;
}
const uint8_t* last_data_sent() { return last_data_sent_; }
FlutterDesktopMessageCallback last_callback_set() {
return last_callback_set_;
FlutterDesktopMessageCallback last_message_callback_set() {
return last_message_callback_set_;
}
FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set() {
return last_destruction_callback_set_;
}
private:
const uint8_t* last_data_sent_ = nullptr;
FlutterDesktopMessageCallback last_callback_set_ = nullptr;
FlutterDesktopMessageCallback last_message_callback_set_ = nullptr;
FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set_ = nullptr;
};
} // namespace
// Tests that the registrar returns a messenger that passes Send through to the
// C API.
TEST(MethodCallTest, MessengerSend) {
TEST(PluginRegistrarTest, MessengerSend) {
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
auto test_api = static_cast<TestApi*>(scoped_api_stub.stub());
......@@ -70,7 +78,7 @@ TEST(MethodCallTest, MessengerSend) {
// Tests that the registrar returns a messenger that passes callback
// registration and unregistration through to the C API.
TEST(MethodCallTest, MessengerSetMessageHandler) {
TEST(PluginRegistrarTest, MessengerSetMessageHandler) {
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
auto test_api = static_cast<TestApi*>(scoped_api_stub.stub());
......@@ -85,11 +93,60 @@ TEST(MethodCallTest, MessengerSetMessageHandler) {
const size_t message_size,
BinaryReply reply) {};
messenger->SetMessageHandler(channel_name, std::move(binary_handler));
EXPECT_NE(test_api->last_callback_set(), nullptr);
EXPECT_NE(test_api->last_message_callback_set(), nullptr);
// Unregister.
messenger->SetMessageHandler(channel_name, nullptr);
EXPECT_EQ(test_api->last_callback_set(), nullptr);
EXPECT_EQ(test_api->last_message_callback_set(), nullptr);
}
// Tests that the registrar manager returns the same instance when getting
// the wrapper for the same reference.
TEST(PluginRegistrarTest, ManagerSameInstance) {
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
auto dummy_registrar_handle =
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance();
EXPECT_EQ(manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle),
manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle));
}
// Tests that the registrar manager returns different objects for different
// references.
TEST(PluginRegistrarTest, ManagerDifferentInstances) {
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
auto dummy_registrar_handle_a =
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
auto dummy_registrar_handle_b =
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(2);
PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance();
EXPECT_NE(manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle_a),
manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle_b));
}
// Tests that the registrar manager deletes wrappers when the underlying
// reference is destroyed.
TEST(PluginRegistrarTest, ManagerRemovesOnDestruction) {
testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique<TestApi>());
auto test_api = static_cast<TestApi*>(scoped_api_stub.stub());
auto dummy_registrar_handle =
reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance();
auto* first_wrapper =
manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle);
// Simulate destruction of the reference.
EXPECT_NE(test_api->last_destruction_callback_set(), nullptr);
test_api->last_destruction_callback_set()(dummy_registrar_handle);
// Requesting the wrapper should now create a new object.
EXPECT_NE(manager->GetRegistrar<PluginRegistrar>(dummy_registrar_handle),
first_wrapper);
}
} // namespace flutter
......@@ -44,6 +44,14 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
return reinterpret_cast<FlutterDesktopMessengerRef>(1);
}
void FlutterDesktopRegistrarSetDestructionHandler(
FlutterDesktopPluginRegistrarRef registrar,
FlutterDesktopOnRegistrarDestroyed callback) {
if (s_stub_implementation) {
s_stub_implementation->RegistrarSetDestructionHandler(callback);
}
}
void FlutterDesktopRegistrarEnableInputBlocking(
FlutterDesktopPluginRegistrarRef registrar,
const char* channel) {
......
......@@ -34,6 +34,10 @@ class StubFlutterApi {
virtual ~StubFlutterApi() {}
// Called for FlutterDesktopRegistrarSetDestructionHandler.
virtual void RegistrarSetDestructionHandler(
FlutterDesktopOnRegistrarDestroyed callback) {}
// Called for FlutterDesktopRegistrarEnableInputBlocking.
virtual void RegistrarEnableInputBlocking(const char* channel) {}
......
......@@ -18,10 +18,19 @@ extern "C" {
// Opaque reference to a plugin registrar.
typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef;
// Function pointer type for registrar destruction callback.
typedef void (*FlutterDesktopOnRegistrarDestroyed)(
FlutterDesktopPluginRegistrarRef);
// Returns the engine messenger associated with this registrar.
FLUTTER_EXPORT FlutterDesktopMessengerRef
FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar);
// Registers a callback to be called when the plugin registrar is destroyed.
FLUTTER_EXPORT void FlutterDesktopRegistrarSetDestructionHandler(
FlutterDesktopPluginRegistrarRef registrar,
FlutterDesktopOnRegistrarDestroyed callback);
// Enables input blocking on the given channel.
//
// If set, then the Flutter window will disable input callbacks
......
......@@ -118,6 +118,9 @@ struct FlutterDesktopPluginRegistrar {
// The handle for the window associated with this registrar.
FlutterDesktopWindow* window;
// Callback to be called on registrar destruction.
FlutterDesktopOnRegistrarDestroyed destruction_handler;
};
// State associated with the messenger used to communicate with the engine.
......@@ -682,6 +685,11 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow(
}
void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) {
FlutterDesktopPluginRegistrarRef registrar =
controller->plugin_registrar.get();
if (registrar->destruction_handler) {
registrar->destruction_handler(registrar);
}
FlutterEngineShutdown(controller->engine);
delete controller;
}
......@@ -832,6 +840,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
return registrar->messenger.get();
}
void FlutterDesktopRegistrarSetDestructionHandler(
FlutterDesktopPluginRegistrarRef registrar,
FlutterDesktopOnRegistrarDestroyed callback) {
registrar->destruction_handler = callback;
}
FlutterDesktopWindowRef FlutterDesktopRegistrarGetWindow(
FlutterDesktopPluginRegistrarRef registrar) {
return registrar->window;
......
......@@ -248,6 +248,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
return registrar->messenger.get();
}
void FlutterDesktopRegistrarSetDestructionHandler(
FlutterDesktopPluginRegistrarRef registrar,
FlutterDesktopOnRegistrarDestroyed callback) {
registrar->destruction_handler = callback;
}
FlutterDesktopViewRef FlutterDesktopRegistrarGetView(
FlutterDesktopPluginRegistrarRef registrar) {
return registrar->window;
......
......@@ -15,6 +15,9 @@ Win32FlutterWindow::Win32FlutterWindow(int width, int height) {
Win32FlutterWindow::~Win32FlutterWindow() {
DestroyRenderSurface();
if (plugin_registrar_ && plugin_registrar_->destruction_handler) {
plugin_registrar_->destruction_handler(plugin_registrar_.get());
}
}
FlutterDesktopViewControllerRef Win32FlutterWindow::CreateWin32FlutterWindow(
......
......@@ -55,6 +55,9 @@ struct FlutterDesktopPluginRegistrar {
// The handle for the window associated with this registrar.
FlutterDesktopView* window;
// Callback to be called on registrar destruction.
FlutterDesktopOnRegistrarDestroyed destruction_handler;
};
// State associated with the messenger used to communicate with the engine.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册