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_