Implemented threadsafe platform channel replies on windows (#36909)
* Implemented threadsafe platform channel replies on windows
* added unit test
* added docstrings
* implemented glfw
* added comments
* made glfw messenger unable to be copied
* stuart feedback 1
* stuart feedback 2: replaced the shared_ptr
* stuart feedback 3
* stuart feedback: remove error log
* Moved FlutterDesktopMessenger to its own file.
* updated licenses
* stuart feedback
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 974f458..d48759e 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -3099,6 +3099,7 @@
FILE: ../../../flutter/shell/platform/windows/external_texture_d3d.h
FILE: ../../../flutter/shell/platform/windows/external_texture_pixelbuffer.cc
FILE: ../../../flutter/shell/platform/windows/external_texture_pixelbuffer.h
+FILE: ../../../flutter/shell/platform/windows/flutter_desktop_messenger.h
FILE: ../../../flutter/shell/platform/windows/flutter_key_map.g.cc
FILE: ../../../flutter/shell/platform/windows/flutter_platform_node_delegate_windows.cc
FILE: ../../../flutter/shell/platform/windows/flutter_platform_node_delegate_windows.h
diff --git a/shell/platform/common/client_wrapper/core_implementations.cc b/shell/platform/common/client_wrapper/core_implementations.cc
index 41be751..1eb5a58 100644
--- a/shell/platform/common/client_wrapper/core_implementations.cc
+++ b/shell/platform/common/client_wrapper/core_implementations.cc
@@ -26,6 +26,11 @@
// ========== binary_messenger_impl.h ==========
namespace {
+
+using FlutterDesktopMessengerScopedLock =
+ std::unique_ptr<FlutterDesktopMessenger,
+ decltype(&FlutterDesktopMessengerUnlock)>;
+
// Passes |message| to |user_data|, which must be a BinaryMessageHandler, along
// with a BinaryReply that will send a response on |message|'s response handle.
//
@@ -36,17 +41,28 @@
const FlutterDesktopMessage* message,
void* user_data) {
auto* response_handle = message->response_handle;
- BinaryReply reply_handler = [messenger, response_handle](
+ auto messenger_ptr = std::shared_ptr<FlutterDesktopMessenger>(
+ FlutterDesktopMessengerAddRef(messenger),
+ &FlutterDesktopMessengerRelease);
+ BinaryReply reply_handler = [messenger_ptr, response_handle](
const uint8_t* reply,
size_t reply_size) mutable {
+ // Note: This lambda can be called on any thread.
+ auto lock = FlutterDesktopMessengerScopedLock(
+ FlutterDesktopMessengerLock(messenger_ptr.get()),
+ &FlutterDesktopMessengerUnlock);
+ if (!FlutterDesktopMessengerIsAvailable(messenger_ptr.get())) {
+ // Drop reply if it comes in after the engine is destroyed.
+ return;
+ }
if (!response_handle) {
std::cerr << "Error: Response can be set only once. Ignoring "
"duplicate response."
<< std::endl;
return;
}
- FlutterDesktopMessengerSendResponse(messenger, response_handle, reply,
- reply_size);
+ FlutterDesktopMessengerSendResponse(messenger_ptr.get(), response_handle,
+ reply, reply_size);
// The engine frees the response handle once
// FlutterDesktopSendMessageResponse is called.
response_handle = nullptr;
diff --git a/shell/platform/common/client_wrapper/testing/stub_flutter_api.cc b/shell/platform/common/client_wrapper/testing/stub_flutter_api.cc
index b9a18ba..28b01d7 100644
--- a/shell/platform/common/client_wrapper/testing/stub_flutter_api.cc
+++ b/shell/platform/common/client_wrapper/testing/stub_flutter_api.cc
@@ -4,6 +4,8 @@
#include "flutter/shell/platform/common/client_wrapper/testing/stub_flutter_api.h"
+#include <cassert>
+
static flutter::testing::StubFlutterApi* s_stub_implementation;
namespace flutter {
@@ -93,6 +95,31 @@
}
}
+FlutterDesktopMessengerRef FlutterDesktopMessengerAddRef(
+ FlutterDesktopMessengerRef messenger) {
+ assert(false); // not implemented
+ return nullptr;
+}
+
+void FlutterDesktopMessengerRelease(FlutterDesktopMessengerRef messenger) {
+ assert(false); // not implemented
+}
+
+bool FlutterDesktopMessengerIsAvailable(FlutterDesktopMessengerRef messenger) {
+ assert(false); // not implemented
+ return false;
+}
+
+FlutterDesktopMessengerRef FlutterDesktopMessengerLock(
+ FlutterDesktopMessengerRef messenger) {
+ assert(false); // not implemented
+ return nullptr;
+}
+
+void FlutterDesktopMessengerUnlock(FlutterDesktopMessengerRef messenger) {
+ assert(false); // not implemented
+}
+
FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
return reinterpret_cast<FlutterDesktopTextureRegistrarRef>(1);
diff --git a/shell/platform/common/public/flutter_messenger.h b/shell/platform/common/public/flutter_messenger.h
index 854ff98..019267c 100644
--- a/shell/platform/common/public/flutter_messenger.h
+++ b/shell/platform/common/public/flutter_messenger.h
@@ -5,6 +5,7 @@
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_PUBLIC_FLUTTER_MESSENGER_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_PUBLIC_FLUTTER_MESSENGER_H_
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -87,6 +88,53 @@
FlutterDesktopMessageCallback callback,
void* user_data);
+// Increments the reference count for the |messenger|.
+//
+// Operation is thread-safe.
+//
+// See also: |FlutterDesktopMessengerRelease|
+FLUTTER_EXPORT FlutterDesktopMessengerRef
+FlutterDesktopMessengerAddRef(FlutterDesktopMessengerRef messenger);
+
+// Decrements the reference count for the |messenger|.
+//
+// Operation is thread-safe.
+//
+// See also: |FlutterDesktopMessengerAddRef|
+FLUTTER_EXPORT void FlutterDesktopMessengerRelease(
+ FlutterDesktopMessengerRef messenger);
+
+// Returns `true` if the |FlutterDesktopMessengerRef| still references a running
+// engine.
+//
+// This check should be made inside of a |FlutterDesktopMessengerLock| and
+// before any other calls are made to the FlutterDesktopMessengerRef when using
+// it from a thread other than the platform thread.
+FLUTTER_EXPORT bool FlutterDesktopMessengerIsAvailable(
+ FlutterDesktopMessengerRef messenger);
+
+// Locks the `FlutterDesktopMessengerRef` ensuring that
+// |FlutterDesktopMessengerIsAvailable| does not change while locked.
+//
+// All calls to the FlutterDesktopMessengerRef from threads other than the
+// platform thread should happen inside of a lock.
+//
+// Operation is thread-safe.
+//
+// Returns the |messenger| value.
+//
+// See also: |FlutterDesktopMessengerUnlock|
+FLUTTER_EXPORT FlutterDesktopMessengerRef
+FlutterDesktopMessengerLock(FlutterDesktopMessengerRef messenger);
+
+// Unlocks the `FlutterDesktopMessengerRef`.
+//
+// Operation is thread-safe.
+//
+// See also: |FlutterDesktopMessengerLock|
+FLUTTER_EXPORT void FlutterDesktopMessengerUnlock(
+ FlutterDesktopMessengerRef messenger);
+
#if defined(__cplusplus)
} // extern "C"
#endif
diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc
index 4106afc..3511162 100644
--- a/shell/platform/embedder/embedder.cc
+++ b/shell/platform/embedder/embedder.cc
@@ -2325,6 +2325,7 @@
return kSuccess;
}
+// Note: This can execute on any thread.
FlutterEngineResult FlutterEngineSendPlatformMessageResponse(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPlatformMessageResponseHandle* handle,
diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc
index 55015cf..2509067 100644
--- a/shell/platform/glfw/flutter_glfw.cc
+++ b/shell/platform/glfw/flutter_glfw.cc
@@ -106,6 +106,10 @@
};
using UniqueAotDataPtr = std::unique_ptr<_FlutterEngineAOTData, AOTDataDeleter>;
+/// Maintains one ref on the FlutterDesktopMessenger's internal reference count.
+using FlutterDesktopMessengerReferenceOwner =
+ std::unique_ptr<FlutterDesktopMessenger,
+ decltype(&FlutterDesktopMessengerRelease)>;
// Struct for storing state of a Flutter engine instance.
struct FlutterDesktopEngineState {
@@ -116,7 +120,8 @@
std::unique_ptr<flutter::EventLoop> event_loop;
// The plugin messenger handle given to API clients.
- std::unique_ptr<FlutterDesktopMessenger> messenger;
+ FlutterDesktopMessengerReferenceOwner messenger = {
+ nullptr, [](FlutterDesktopMessengerRef ref) {}};
// Message dispatch manager for messages from the Flutter engine.
std::unique_ptr<flutter::IncomingMessageDispatcher> message_dispatcher;
@@ -149,10 +154,75 @@
// State associated with the messenger used to communicate with the engine.
struct FlutterDesktopMessenger {
+ FlutterDesktopMessenger() = default;
+
+ /// Increments the reference count.
+ ///
+ /// Thread-safe.
+ void AddRef() { ref_count_.fetch_add(1); }
+
+ /// Decrements the reference count and deletes the object if the count has
+ /// gone to zero.
+ ///
+ /// Thread-safe.
+ void Release() {
+ int32_t old_count = ref_count_.fetch_sub(1);
+ if (old_count <= 1) {
+ delete this;
+ }
+ }
+
+ /// Getter for the engine field.
+ FlutterDesktopEngineState* GetEngine() const { return engine_; }
+
+ /// Setter for the engine field.
+ /// Thread-safe.
+ void SetEngine(FlutterDesktopEngineState* engine) {
+ std::scoped_lock lock(mutex_);
+ engine_ = engine;
+ }
+
+ /// Returns the mutex associated with the |FlutterDesktopMessenger|.
+ ///
+ /// This mutex is used to synchronize reading or writing state inside the
+ /// |FlutterDesktopMessenger| (ie |engine_|).
+ std::mutex& GetMutex() { return mutex_; }
+
+ FlutterDesktopMessenger(const FlutterDesktopMessenger& value) = delete;
+ FlutterDesktopMessenger& operator=(const FlutterDesktopMessenger& value) =
+ delete;
+
+ private:
// The engine that backs this messenger.
- FlutterDesktopEngineState* engine;
+ FlutterDesktopEngineState* engine_;
+ std::atomic<int32_t> ref_count_ = 0;
+ std::mutex mutex_;
};
+FlutterDesktopMessengerRef FlutterDesktopMessengerAddRef(
+ FlutterDesktopMessengerRef messenger) {
+ messenger->AddRef();
+ return messenger;
+}
+
+void FlutterDesktopMessengerRelease(FlutterDesktopMessengerRef messenger) {
+ messenger->Release();
+}
+
+bool FlutterDesktopMessengerIsAvailable(FlutterDesktopMessengerRef messenger) {
+ return messenger->GetEngine() != nullptr;
+}
+
+FlutterDesktopMessengerRef FlutterDesktopMessengerLock(
+ FlutterDesktopMessengerRef messenger) {
+ messenger->GetMutex().lock();
+ return messenger;
+}
+
+void FlutterDesktopMessengerUnlock(FlutterDesktopMessengerRef messenger) {
+ messenger->GetMutex().unlock();
+}
+
// Retrieves state bag for the window in question from the GLFWWindow.
static FlutterDesktopWindowControllerState* GetWindowController(
GLFWwindow* window) {
@@ -743,8 +813,10 @@
static void SetUpCommonEngineState(FlutterDesktopEngineState* state,
GLFWwindow* window) {
// Messaging.
- state->messenger = std::make_unique<FlutterDesktopMessenger>();
- state->messenger->engine = state;
+ state->messenger = FlutterDesktopMessengerReferenceOwner(
+ FlutterDesktopMessengerAddRef(new FlutterDesktopMessenger()),
+ &FlutterDesktopMessengerRelease);
+ state->messenger->SetEngine(state);
state->message_dispatcher =
std::make_unique<flutter::IncomingMessageDispatcher>(
state->messenger.get());
@@ -846,6 +918,7 @@
}
void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) {
+ controller->engine->messenger->SetEngine(nullptr);
FlutterDesktopPluginRegistrarRef registrar =
controller->engine->plugin_registrar.get();
if (registrar->destruction_handler) {
@@ -1045,7 +1118,8 @@
FlutterPlatformMessageResponseHandle* response_handle = nullptr;
if (reply != nullptr && user_data != nullptr) {
FlutterEngineResult result = FlutterPlatformMessageCreateResponseHandle(
- messenger->engine->flutter_engine, reply, user_data, &response_handle);
+ messenger->GetEngine()->flutter_engine, reply, user_data,
+ &response_handle);
if (result != kSuccess) {
std::cout << "Failed to create response handle\n";
return false;
@@ -1061,11 +1135,11 @@
};
FlutterEngineResult message_result = FlutterEngineSendPlatformMessage(
- messenger->engine->flutter_engine, &platform_message);
+ messenger->GetEngine()->flutter_engine, &platform_message);
if (response_handle != nullptr) {
FlutterPlatformMessageReleaseResponseHandle(
- messenger->engine->flutter_engine, response_handle);
+ messenger->GetEngine()->flutter_engine, response_handle);
}
return message_result == kSuccess;
@@ -1084,16 +1158,16 @@
const FlutterDesktopMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
- FlutterEngineSendPlatformMessageResponse(messenger->engine->flutter_engine,
- handle, data, data_length);
+ FlutterEngineSendPlatformMessageResponse(
+ messenger->GetEngine()->flutter_engine, handle, data, data_length);
}
void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger,
const char* channel,
FlutterDesktopMessageCallback callback,
void* user_data) {
- messenger->engine->message_dispatcher->SetMessageCallback(channel, callback,
- user_data);
+ messenger->GetEngine()->message_dispatcher->SetMessageCallback(
+ channel, callback, user_data);
}
FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar(
diff --git a/shell/platform/windows/flutter_desktop_messenger.h b/shell/platform/windows/flutter_desktop_messenger.h
new file mode 100644
index 0000000..8920068
--- /dev/null
+++ b/shell/platform/windows/flutter_desktop_messenger.h
@@ -0,0 +1,83 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_DESKTOP_MESSENGER_H_
+#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_DESKTOP_MESSENGER_H_
+
+#include <atomic>
+#include <mutex>
+
+#include "flutter/shell/platform/common/public/flutter_messenger.h"
+
+namespace flutter {
+
+class FlutterWindowsEngine;
+
+/// A messenger object used to invoke platform messages.
+///
+/// On Windows, the message handler is essentially the |FlutterWindowsEngine|,
+/// this allows a handle to the |FlutterWindowsEngine| that will become
+/// invalidated if the |FlutterWindowsEngine| is destroyed.
+class FlutterDesktopMessenger {
+ public:
+ FlutterDesktopMessenger() = default;
+
+ /// Convert to FlutterDesktopMessengerRef.
+ FlutterDesktopMessengerRef ToRef() {
+ return reinterpret_cast<FlutterDesktopMessengerRef>(this);
+ }
+
+ /// Convert from FlutterDesktopMessengerRef.
+ static FlutterDesktopMessenger* FromRef(FlutterDesktopMessengerRef ref) {
+ return reinterpret_cast<FlutterDesktopMessenger*>(ref);
+ }
+
+ /// Getter for the engine field.
+ flutter::FlutterWindowsEngine* GetEngine() const { return engine; }
+
+ /// Setter for the engine field.
+ /// Thread-safe.
+ void SetEngine(flutter::FlutterWindowsEngine* arg_engine) {
+ std::scoped_lock lock(mutex_);
+ engine = arg_engine;
+ }
+
+ /// Increments the reference count.
+ ///
+ /// Thread-safe.
+ FlutterDesktopMessenger* AddRef() {
+ ref_count_.fetch_add(1);
+ return this;
+ }
+
+ /// Decrements the reference count and deletes the object if the count has
+ /// gone to zero.
+ ///
+ /// Thread-safe.
+ void Release() {
+ int32_t old_count = ref_count_.fetch_sub(1);
+ if (old_count <= 1) {
+ delete this;
+ }
+ }
+
+ /// Returns the mutex associated with the |FlutterDesktopMessenger|.
+ ///
+ /// This mutex is used to synchronize reading or writing state inside the
+ /// |FlutterDesktopMessenger| (ie |engine|).
+ std::mutex& GetMutex() { return mutex_; }
+
+ FlutterDesktopMessenger(const FlutterDesktopMessenger& value) = delete;
+ FlutterDesktopMessenger& operator=(const FlutterDesktopMessenger& value) =
+ delete;
+
+ private:
+ // The engine that owns this state object.
+ flutter::FlutterWindowsEngine* engine = nullptr;
+ std::mutex mutex_;
+ std::atomic<int32_t> ref_count_ = 0;
+};
+} // namespace flutter
+
+#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_STATE_H_
diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc
index 0d8ca85..0756efc 100644
--- a/shell/platform/windows/flutter_windows.cc
+++ b/shell/platform/windows/flutter_windows.cc
@@ -262,8 +262,9 @@
const size_t message_size,
const FlutterDesktopBinaryReply reply,
void* user_data) {
- return messenger->engine->SendPlatformMessage(channel, message, message_size,
- reply, user_data);
+ return flutter::FlutterDesktopMessenger::FromRef(messenger)
+ ->GetEngine()
+ ->SendPlatformMessage(channel, message, message_size, reply, user_data);
}
bool FlutterDesktopMessengerSend(FlutterDesktopMessengerRef messenger,
@@ -279,15 +280,45 @@
const FlutterDesktopMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
- messenger->engine->SendPlatformMessageResponse(handle, data, data_length);
+ flutter::FlutterDesktopMessenger::FromRef(messenger)
+ ->GetEngine()
+ ->SendPlatformMessageResponse(handle, data, data_length);
}
void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger,
const char* channel,
FlutterDesktopMessageCallback callback,
void* user_data) {
- messenger->engine->message_dispatcher()->SetMessageCallback(channel, callback,
- user_data);
+ flutter::FlutterDesktopMessenger::FromRef(messenger)
+ ->GetEngine()
+ ->message_dispatcher()
+ ->SetMessageCallback(channel, callback, user_data);
+}
+
+FlutterDesktopMessengerRef FlutterDesktopMessengerAddRef(
+ FlutterDesktopMessengerRef messenger) {
+ return flutter::FlutterDesktopMessenger::FromRef(messenger)
+ ->AddRef()
+ ->ToRef();
+}
+
+void FlutterDesktopMessengerRelease(FlutterDesktopMessengerRef messenger) {
+ flutter::FlutterDesktopMessenger::FromRef(messenger)->Release();
+}
+
+bool FlutterDesktopMessengerIsAvailable(FlutterDesktopMessengerRef messenger) {
+ return flutter::FlutterDesktopMessenger::FromRef(messenger)->GetEngine() !=
+ nullptr;
+}
+
+FlutterDesktopMessengerRef FlutterDesktopMessengerLock(
+ FlutterDesktopMessengerRef messenger) {
+ flutter::FlutterDesktopMessenger::FromRef(messenger)->GetMutex().lock();
+ return messenger;
+}
+
+void FlutterDesktopMessengerUnlock(FlutterDesktopMessengerRef messenger) {
+ flutter::FlutterDesktopMessenger::FromRef(messenger)->GetMutex().unlock();
}
FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar(
diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc
index 24b17a2..1cd2fc4 100644
--- a/shell/platform/windows/flutter_windows_engine.cc
+++ b/shell/platform/windows/flutter_windows_engine.cc
@@ -179,14 +179,16 @@
});
// Set up the legacy structs backing the API handles.
- messenger_ = std::make_unique<FlutterDesktopMessenger>();
- messenger_->engine = this;
+ messenger_ =
+ fml::RefPtr<FlutterDesktopMessenger>(new FlutterDesktopMessenger());
+ messenger_->SetEngine(this);
plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
plugin_registrar_->engine = this;
- messenger_wrapper_ = std::make_unique<BinaryMessengerImpl>(messenger_.get());
+ messenger_wrapper_ =
+ std::make_unique<BinaryMessengerImpl>(messenger_->ToRef());
message_dispatcher_ =
- std::make_unique<IncomingMessageDispatcher>(messenger_.get());
+ std::make_unique<IncomingMessageDispatcher>(messenger_->ToRef());
message_dispatcher_->SetMessageCallback(
kAccessibilityChannelName,
[](FlutterDesktopMessengerRef messenger,
@@ -210,6 +212,7 @@
}
FlutterWindowsEngine::~FlutterWindowsEngine() {
+ messenger_->SetEngine(nullptr);
Stop();
}
diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h
index 64a4f0f..3b6fdf1 100644
--- a/shell/platform/windows/flutter_windows_engine.h
+++ b/shell/platform/windows/flutter_windows_engine.h
@@ -20,6 +20,7 @@
#include "flutter/shell/platform/common/incoming_message_dispatcher.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/angle_surface_manager.h"
+#include "flutter/shell/platform/windows/flutter_desktop_messenger.h"
#include "flutter/shell/platform/windows/flutter_project_bundle.h"
#include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
@@ -124,7 +125,7 @@
// Sets switches member to the given switches.
void SetSwitches(const std::vector<std::string>& switches);
- FlutterDesktopMessengerRef messenger() { return messenger_.get(); }
+ FlutterDesktopMessengerRef messenger() { return messenger_->ToRef(); }
IncomingMessageDispatcher* message_dispatcher() {
return message_dispatcher_.get();
@@ -283,7 +284,7 @@
std::unique_ptr<TaskRunner> task_runner_;
// The plugin messenger handle given to API clients.
- std::unique_ptr<FlutterDesktopMessenger> messenger_;
+ fml::RefPtr<flutter::FlutterDesktopMessenger> messenger_;
// A wrapper around messenger_ for interacting with client_wrapper-level APIs.
std::unique_ptr<BinaryMessengerImpl> messenger_wrapper_;
diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc
index 5649d58..9714bd8 100644
--- a/shell/platform/windows/flutter_windows_engine_unittests.cc
+++ b/shell/platform/windows/flutter_windows_engine_unittests.cc
@@ -311,6 +311,60 @@
}
}
+TEST_F(FlutterWindowsEngineTest, PlatformMessageRespondOnDifferentThread) {
+ FlutterDesktopEngineProperties properties = {};
+ properties.assets_path = GetContext().GetAssetsPath().c_str();
+ properties.icu_data_path = GetContext().GetIcuDataPath().c_str();
+ properties.dart_entrypoint = "hiPlatformChannels";
+
+ FlutterProjectBundle project(properties);
+ auto engine = std::make_unique<FlutterWindowsEngine>(project);
+
+ EngineModifier modifier(engine.get());
+ modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
+
+ auto binary_messenger =
+ std::make_unique<BinaryMessengerImpl>(engine->messenger());
+
+ engine->Run();
+ bool did_call_callback = false;
+ bool did_call_reply = false;
+ bool did_call_dart_reply = false;
+ std::string channel = "hi";
+ std::unique_ptr<std::thread> reply_thread;
+ binary_messenger->SetMessageHandler(
+ channel,
+ [&did_call_callback, &did_call_dart_reply, &reply_thread](
+ const uint8_t* message, size_t message_size, BinaryReply reply) {
+ if (message_size == 5) {
+ EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
+ reply_thread.reset(new std::thread([reply = std::move(reply)]() {
+ char response[] = {'b', 'y', 'e'};
+ reply(reinterpret_cast<uint8_t*>(response), 3);
+ }));
+ did_call_callback = true;
+ } else {
+ EXPECT_EQ(message_size, 3);
+ EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
+ did_call_dart_reply = true;
+ }
+ });
+ char payload[] = {'h', 'e', 'l', 'l', 'o'};
+ binary_messenger->Send(
+ channel, reinterpret_cast<uint8_t*>(payload), 5,
+ [&did_call_reply](const uint8_t* reply, size_t reply_size) {
+ EXPECT_EQ(reply_size, 5);
+ EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
+ did_call_reply = true;
+ });
+ // Rely on timeout mechanism in CI.
+ while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
+ engine->task_runner()->ProcessTasks();
+ }
+ ASSERT_TRUE(reply_thread);
+ reply_thread->join();
+}
+
TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithResponse) {
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
EngineModifier modifier(engine.get());
diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h
index 8919545..8c1e365 100644
--- a/shell/platform/windows/public/flutter_windows.h
+++ b/shell/platform/windows/public/flutter_windows.h
@@ -181,6 +181,12 @@
const char* plugin_name);
// Returns the messenger associated with the engine.
+//
+// This does not provide an owning reference, so should *not* be balanced with a
+// call to |FlutterDesktopMessengerRelease|.
+//
+// Callers should use |FlutterDesktopMessengerAddRef| if the returned pointer
+// will potentially outlive 'engine', such as when passing it to another thread.
FLUTTER_EXPORT FlutterDesktopMessengerRef
FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine);
diff --git a/shell/platform/windows/window_state.h b/shell/platform/windows/window_state.h
index 56897f7..13b282a 100644
--- a/shell/platform/windows/window_state.h
+++ b/shell/platform/windows/window_state.h
@@ -33,11 +33,4 @@
flutter::FlutterWindowsEngine* engine = nullptr;
};
-// Wrapper to distinguish the messenger ref from the engine ref given out
-// in the C API.
-struct FlutterDesktopMessenger {
- // The engine that owns this state object.
- flutter::FlutterWindowsEngine* engine = nullptr;
-};
-
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_STATE_H_