From f303fbaa9069664ff846a7212ddf7eb4ff83d4b2 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 4 Aug 2022 14:16:29 -0700 Subject: [PATCH] Started handling messages from background isolates. --- ci/licenses_golden/licenses_flutter | 3 + fml/memory/threadsafe_unique_ptr.h | 111 ++++++++++++++++++ lib/ui/BUILD.gn | 2 + lib/ui/dart_ui.cc | 3 + lib/ui/platform_dispatcher.dart | 28 ++++- lib/ui/ui_dart_state.cc | 24 +++- lib/ui/ui_dart_state.h | 20 +++- lib/ui/window/platform_configuration.cc | 82 +++++++++++-- lib/ui/window/platform_configuration.h | 25 ++++ .../platform_message_response_dart_port.cc | 72 ++++++++++++ .../platform_message_response_dart_port.h | 34 ++++++ lib/web_ui/lib/platform_dispatcher.dart | 10 ++ .../lib/src/engine/platform_dispatcher.dart | 20 ++++ runtime/dart_isolate.cc | 8 +- runtime/dart_isolate_group_data.cc | 17 +++ runtime/dart_isolate_group_data.h | 18 ++- shell/common/engine.cc | 1 + shell/common/shell.cc | 7 +- .../ios/platform_message_handler_ios.mm | 5 +- 19 files changed, 471 insertions(+), 19 deletions(-) create mode 100644 fml/memory/threadsafe_unique_ptr.h create mode 100644 lib/ui/window/platform_message_response_dart_port.cc create mode 100644 lib/ui/window/platform_message_response_dart_port.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1e5fa58c911ea..b35e5131fa0a8 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -279,6 +279,7 @@ FILE: ../../../flutter/fml/memory/task_runner_checker.cc FILE: ../../../flutter/fml/memory/task_runner_checker.h FILE: ../../../flutter/fml/memory/task_runner_checker_unittest.cc FILE: ../../../flutter/fml/memory/thread_checker.h +FILE: ../../../flutter/fml/memory/threadsafe_unique_ptr.h FILE: ../../../flutter/fml/memory/weak_ptr.h FILE: ../../../flutter/fml/memory/weak_ptr_internal.cc FILE: ../../../flutter/fml/memory/weak_ptr_internal.h @@ -1054,6 +1055,8 @@ FILE: ../../../flutter/lib/ui/window/platform_message_response.cc FILE: ../../../flutter/lib/ui/window/platform_message_response.h FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.cc FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.h +FILE: ../../../flutter/lib/ui/window/platform_message_response_dart_port.cc +FILE: ../../../flutter/lib/ui/window/platform_message_response_dart_port.h FILE: ../../../flutter/lib/ui/window/pointer_data.cc FILE: ../../../flutter/lib/ui/window/pointer_data.h FILE: ../../../flutter/lib/ui/window/pointer_data_packet.cc diff --git a/fml/memory/threadsafe_unique_ptr.h b/fml/memory/threadsafe_unique_ptr.h new file mode 100644 index 0000000000000..2b8925976d789 --- /dev/null +++ b/fml/memory/threadsafe_unique_ptr.h @@ -0,0 +1,111 @@ +// 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_FML_THREADSAFE_UNIQUE_PTR_H_ +#define FLUTTER_FML_THREADSAFE_UNIQUE_PTR_H_ + +#include +#include + +#include "flutter/fml/macros.h" + +namespace fml { + +/// A single-owner smart pointer that allows weak references that can be +/// accessed on different threads. +/// This smart-pointer makes the following guarantees: +/// * There is only ever one threadsafe_unique_ptr per object. +/// * The object is deleted when ~threadsafe_unique_ptr() is called. +/// * The object will not be deleted whilst another thread has a lock_ptr. +/// * The thread that owns the object can access it without a lock. +/// WARNING: weak_ptr's should only be used to invoke thread-safe methods. +template +class threadsafe_unique_ptr { + private: + struct Data { + Data(std::unique_ptr&& arg_ptr) : ptr(std::move(arg_ptr)) {} + std::unique_ptr ptr; + std::mutex mutex; + }; + + public: + threadsafe_unique_ptr() + : data_(new Data(std::unique_ptr())), fast_ptr_(nullptr) {} + + threadsafe_unique_ptr(std::unique_ptr&& ptr) + : data_(new Data(std::move(ptr))) { + fast_ptr_ = data_->ptr.get(); + } + + threadsafe_unique_ptr(threadsafe_unique_ptr&& ptr) + : data_(std::move(ptr.data_)), fast_ptr_(std::move(ptr.fast_ptr_)) {} + + threadsafe_unique_ptr& operator=(threadsafe_unique_ptr&& rvalue) { + data_ = std::move(rvalue.data_); + fast_ptr_ = std::move(rvalue.fast_ptr_); + return *this; + } + + ~threadsafe_unique_ptr() { + if (data_) { + std::scoped_lock lock(data_->mutex); + data_->ptr.reset(); + } + } + + T* operator->() const { return fast_ptr_; } + + T* get() const { return fast_ptr_; } + + operator bool() const { return fast_ptr_ != nullptr; } + + class lock_ptr; + + /// A non-owning smart pointer for a `threadsafe_unique_ptr`. + class weak_ptr { + public: + weak_ptr() {} + + weak_ptr(std::weak_ptr weak_ptr) : weak_ptr_(weak_ptr) {} + + private: + friend class lock_ptr; + std::weak_ptr weak_ptr_; + }; + + /// A temporary owning smart pointer for a `threadsafe_unique_ptr`. This + /// guarantees that the object will not be deleted while in scope. + class lock_ptr { + public: + lock_ptr(const weak_ptr& weak) : strong_ptr_(weak.weak_ptr_.lock()) { + if (strong_ptr_) { + lock_ = std::unique_lock(strong_ptr_->mutex); + } + } + + T* operator->() { return strong_ptr_ ? strong_ptr_->ptr.get() : nullptr; } + + operator bool() const { + return strong_ptr_ ? static_cast(strong_ptr_->ptr) : false; + } + + private: + FML_DISALLOW_COPY_AND_ASSIGN(lock_ptr); + std::shared_ptr strong_ptr_; + std::unique_lock lock_; + }; + + weak_ptr GetWeakPtr() const { return weak_ptr(data_); } + + private: + FML_DISALLOW_COPY_AND_ASSIGN(threadsafe_unique_ptr); + std::shared_ptr data_; + // Clients that own the threadsafe_unique_ptr can access the pointer directly + // since there is no risk that it will be deleted whilst being accessed. + T* fast_ptr_; +}; + +} // namespace fml + +#endif // FLUTTER_FML_THREADSAFE_UNIQUE_PTR_H_ diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 81433a10e754e..a55c52108fcc5 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -124,6 +124,8 @@ source_set("ui") { "window/platform_message_response.h", "window/platform_message_response_dart.cc", "window/platform_message_response_dart.h", + "window/platform_message_response_dart_port.cc", + "window/platform_message_response_dart_port.h", "window/pointer_data.cc", "window/pointer_data.h", "window/pointer_data_packet.cc", diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 61d35c5cd2a5c..eff4f7d1bfc60 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -97,6 +97,9 @@ typedef CanvasPath Path; V(PlatformConfigurationNativeApi::ComputePlatformResolvedLocale, 1) \ V(PlatformConfigurationNativeApi::SendPlatformMessage, 3) \ V(PlatformConfigurationNativeApi::RespondToPlatformMessage, 2) \ + V(PlatformConfigurationNativeApi::RegisterRootIsolate, 0) \ + V(PlatformConfigurationNativeApi::RegisterBackgroundIsolate, 1) \ + V(PlatformConfigurationNativeApi::SendPortPlatformMessage, 4) \ V(DartRuntimeHooks::Logger_PrintDebugString, 1) \ V(DartRuntimeHooks::Logger_PrintString, 1) \ V(DartRuntimeHooks::ScheduleMicrotask, 1) \ diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 1a86c5846bf46..c072ab5bceda9 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -528,12 +528,38 @@ class PlatformDispatcher { } } - String? _sendPlatformMessage(String name,PlatformMessageResponseCallback? callback, ByteData? data) => + String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) => __sendPlatformMessage(name, callback, data); @FfiNative('PlatformConfigurationNativeApi::SendPlatformMessage') external static String? __sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data); + void sendPortPlatformMessage( + String name, + ByteData? data, + int identifier, + SendPort port) { + final String? error = + _sendPortPlatformMessage(name, identifier, port.nativePort, data); + if (error != null) { + throw Exception(error); + } + } + + String? _sendPortPlatformMessage(String name, int identifier, int port, ByteData? data) => + __sendPortPlatformMessage(name, identifier, port, data); + + @FfiNative('PlatformConfigurationNativeApi::SendPortPlatformMessage') + external static String? __sendPortPlatformMessage(String name, int identifier, int port, ByteData? data); + + int registerRootIsolate() => __registerRootIsolate(); + @FfiNative('PlatformConfigurationNativeApi::RegisterRootIsolate') + external static int __registerRootIsolate(); + + void registerBackgroundIsolate(int rootIsolateId) => __registerBackgroundIsolate(rootIsolateId); + @FfiNative('PlatformConfigurationNativeApi::RegisterBackgroundIsolate') + external static void __registerBackgroundIsolate(int rootIsolateId); + /// Called whenever this platform dispatcher receives a message from a /// platform-specific plugin. /// diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index ed60f8cbf2c5b..e0155c76b3b00 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -106,7 +106,8 @@ UIDartState* UIDartState::Current() { } void UIDartState::SetPlatformConfiguration( - std::unique_ptr platform_configuration) { + fml::threadsafe_unique_ptr platform_configuration) { + FML_DCHECK(IsRootIsolate()); platform_configuration_ = std::move(platform_configuration); if (platform_configuration_) { platform_configuration_->client()->UpdateIsolateDescription(debug_name_, @@ -114,6 +115,13 @@ void UIDartState::SetPlatformConfiguration( } } +void UIDartState::SetWeakPlatformConfiguration( + fml::threadsafe_unique_ptr::weak_ptr + platform_configuration) { + FML_DCHECK(!IsRootIsolate()); + weak_platform_configuration_ = platform_configuration; +} + const TaskRunners& UIDartState::GetTaskRunners() const { return context_.task_runners; } @@ -214,4 +222,18 @@ bool UIDartState::enable_skparagraph() const { return enable_skparagraph_; } +void UIDartState::HandlePlatformMessage( + std::unique_ptr message) { + if (platform_configuration_) { + platform_configuration_->client()->HandlePlatformMessage( + std::move(message)); + } else { + fml::threadsafe_unique_ptr::lock_ptr lock( + weak_platform_configuration_); + if (lock) { + lock->client()->HandlePlatformMessage(std::move(message)); + } + } +} + } // namespace flutter diff --git a/lib/ui/ui_dart_state.h b/lib/ui/ui_dart_state.h index bdcb49beadf4a..ca145e0046efb 100644 --- a/lib/ui/ui_dart_state.h +++ b/lib/ui/ui_dart_state.h @@ -13,6 +13,7 @@ #include "flutter/common/task_runners.h" #include "flutter/flow/skia_gpu_object.h" #include "flutter/fml/build_config.h" +#include "flutter/fml/memory/threadsafe_unique_ptr.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/lib/ui/io_manager.h" @@ -30,6 +31,7 @@ namespace flutter { class FontSelector; class ImageGeneratorRegistry; class PlatformConfiguration; +class PlatformMessage; class UIDartState : public tonic::DartState { public: @@ -106,6 +108,18 @@ class UIDartState : public tonic::DartState { return platform_configuration_.get(); } + fml::threadsafe_unique_ptr::weak_ptr + GetWeakPlatformConfiguration() const { + return platform_configuration_ ? platform_configuration_.GetWeakPtr() + : weak_platform_configuration_; + } + + void SetWeakPlatformConfiguration( + fml::threadsafe_unique_ptr::weak_ptr + platform_configuration); + + void HandlePlatformMessage(std::unique_ptr message); + const TaskRunners& GetTaskRunners() const; void ScheduleMicrotask(Dart_Handle handle); @@ -167,7 +181,7 @@ class UIDartState : public tonic::DartState { ~UIDartState() override; void SetPlatformConfiguration( - std::unique_ptr platform_configuration); + fml::threadsafe_unique_ptr platform_configuration); const std::string& GetAdvisoryScriptURI() const; @@ -180,7 +194,9 @@ class UIDartState : public tonic::DartState { Dart_Port main_port_ = ILLEGAL_PORT; const bool is_root_isolate_; std::string debug_name_; - std::unique_ptr platform_configuration_; + fml::threadsafe_unique_ptr platform_configuration_; + fml::threadsafe_unique_ptr::weak_ptr + weak_platform_configuration_; tonic::DartMicrotaskQueue microtask_queue_; UnhandledExceptionCallback unhandled_exception_callback_; LogMessageCallback log_message_callback_; diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 4bee227549842..76d3ebc8d56ca 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -9,6 +9,7 @@ #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/platform_message_response_dart.h" +#include "flutter/lib/ui/window/platform_message_response_dart_port.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" #include "third_party/tonic/converter/dart_converter.h" @@ -284,6 +285,25 @@ void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) { ->SetNeedsReportTimings(value); } +namespace { +void HandlePlatformMessage( + UIDartState* dart_state, + const std::string& name, + Dart_Handle data_handle, + const fml::RefPtr& response) { + if (Dart_IsNull(data_handle)) { + dart_state->HandlePlatformMessage( + std::make_unique(name, response)); + } else { + tonic::DartByteData data(data_handle); + const uint8_t* buffer = static_cast(data.data()); + dart_state->HandlePlatformMessage(std::make_unique( + name, fml::MallocMapping::Copy(buffer, data.length_in_bytes()), + response)); + } +} +} // namespace + Dart_Handle PlatformConfigurationNativeApi::SendPlatformMessage( const std::string& name, Dart_Handle callback, @@ -292,7 +312,8 @@ Dart_Handle PlatformConfigurationNativeApi::SendPlatformMessage( if (!dart_state->platform_configuration()) { return tonic::ToDart( - "Platform messages can only be sent from the main isolate"); + "Sending messages off the root isolate should happen via " + "SendPortPlatformMessage."); } fml::RefPtr response; @@ -301,18 +322,31 @@ Dart_Handle PlatformConfigurationNativeApi::SendPlatformMessage( tonic::DartPersistentValue(dart_state, callback), dart_state->GetTaskRunners().GetUITaskRunner(), name); } - if (Dart_IsNull(data_handle)) { - dart_state->platform_configuration()->client()->HandlePlatformMessage( - std::make_unique(name, response)); - } else { - tonic::DartByteData data(data_handle); - const uint8_t* buffer = static_cast(data.data()); - dart_state->platform_configuration()->client()->HandlePlatformMessage( - std::make_unique( - name, fml::MallocMapping::Copy(buffer, data.length_in_bytes()), - response)); + HandlePlatformMessage(dart_state, name, data_handle, response); + + return Dart_Null(); +} + +Dart_Handle PlatformConfigurationNativeApi::SendPortPlatformMessage( + const std::string& name, + Dart_Handle identifier, + Dart_Handle send_port, + Dart_Handle data_handle) { + // This can be executed on any isolate. + UIDartState* dart_state = UIDartState::Current(); + + int64_t c_send_port = tonic::DartConverter::FromDart(send_port); + if (c_send_port == ILLEGAL_PORT) { + return tonic::ToDart("Invalid port specified"); } + fml::RefPtr response = + fml::MakeRefCounted( + c_send_port, tonic::DartConverter::FromDart(identifier), + name); + + HandlePlatformMessage(dart_state, name, data_handle, response); + return Dart_Null(); } @@ -391,4 +425,30 @@ std::string PlatformConfigurationNativeApi::DefaultRouteName() { ->DefaultRouteName(); } +int64_t PlatformConfigurationNativeApi::RegisterRootIsolate() { + UIDartState* dart_state = UIDartState::Current(); + FML_DCHECK(dart_state && dart_state->IsRootIsolate()); + if (dart_state->IsRootIsolate()) { + int64_t identifier = reinterpret_cast(dart_state); + (*static_cast*>( + Dart_CurrentIsolateGroupData())) + ->SetPlatformConfiguration(identifier, + dart_state->GetWeakPlatformConfiguration()); + return identifier; + } else { + return 0; + } +} + +void PlatformConfigurationNativeApi::RegisterBackgroundIsolate( + int64_t isolate_id) { + UIDartState* dart_state = UIDartState::Current(); + FML_DCHECK(dart_state && !dart_state->IsRootIsolate()); + auto weak_platform_configuration = + (*static_cast*>( + Dart_CurrentIsolateGroupData())) + ->GetPlatformConfiguration(isolate_id); + dart_state->SetWeakPlatformConfiguration(weak_platform_configuration); +} + } // namespace flutter diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index ee90789866633..cf739c75b7831 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -12,6 +12,7 @@ #include #include "flutter/assets/asset_manager.h" +#include "flutter/fml/memory/threadsafe_unique_ptr.h" #include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/semantics/semantics_update.h" #include "flutter/lib/ui/window/pointer_data_packet.h" @@ -442,6 +443,21 @@ class PlatformConfiguration final { pending_responses_; }; +//---------------------------------------------------------------------------- +/// An inteface that the result of `Dart_CurrentIsolateGroupData` should +/// implement for registering background isolates to work. +class PlatformConfigurationStorage { + public: + virtual ~PlatformConfigurationStorage() = default; + virtual void SetPlatformConfiguration( + int64_t root_isolate_id, + fml::threadsafe_unique_ptr::weak_ptr + platform_configuration) = 0; + + virtual fml::threadsafe_unique_ptr::weak_ptr + GetPlatformConfiguration(int64_t root_isolate_id) const = 0; +}; + //---------------------------------------------------------------------------- // API exposed as FFI calls in Dart. // @@ -475,8 +491,17 @@ class PlatformConfigurationNativeApi { Dart_Handle callback, Dart_Handle data_handle); + static Dart_Handle SendPortPlatformMessage(const std::string& name, + Dart_Handle identifier, + Dart_Handle send_port, + Dart_Handle data_handle); + static void RespondToPlatformMessage(int response_id, const tonic::DartByteData& data); + + static int64_t RegisterRootIsolate(); + + static void RegisterBackgroundIsolate(int64_t isolate_id); }; } // namespace flutter diff --git a/lib/ui/window/platform_message_response_dart_port.cc b/lib/ui/window/platform_message_response_dart_port.cc new file mode 100644 index 0000000000000..17801733ca9b4 --- /dev/null +++ b/lib/ui/window/platform_message_response_dart_port.cc @@ -0,0 +1,72 @@ +// 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. + +#include "flutter/lib/ui/window/platform_message_response_dart_port.h" + +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/trace_event.h" +#include "third_party/dart/runtime/include/dart_native_api.h" +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_state.h" +#include "third_party/tonic/logging/dart_invoke.h" +#include "third_party/tonic/typed_data/dart_byte_data.h" + +namespace flutter { +namespace { +void FreeFinalizer(void* isolate_callback_data, void* peer) { + free(peer); +} +} // namespace + +PlatformMessageResponseDartPort::PlatformMessageResponseDartPort( + Dart_Port send_port, + int64_t identifier, + const std::string& channel) + : send_port_(send_port), identifier_(identifier), channel_(channel) { + FML_DCHECK(send_port != ILLEGAL_PORT); +} + +void PlatformMessageResponseDartPort::Complete( + std::unique_ptr data) { + is_complete_ = true; + Dart_CObject response_identifier = { + .type = Dart_CObject_kInt64, + }; + response_identifier.value.as_int64 = identifier_; + Dart_CObject response_data = { + .type = Dart_CObject_kExternalTypedData, + }; + uint8_t* copy = static_cast(malloc(data->GetSize())); + memcpy(copy, data->GetMapping(), data->GetSize()); + response_data.value.as_external_typed_data.type = Dart_TypedData_kUint8; + response_data.value.as_external_typed_data.length = data->GetSize(); + response_data.value.as_external_typed_data.data = copy; + response_data.value.as_external_typed_data.peer = copy; + response_data.value.as_external_typed_data.callback = FreeFinalizer; + + Dart_CObject* response_values[2] = {&response_identifier, &response_data}; + + Dart_CObject response = { + .type = Dart_CObject_kArray, + }; + response.value.as_array.length = 2; + response.value.as_array.values = response_values; + + bool did_send = Dart_PostCObject(send_port_, &response); + FML_CHECK(did_send); +} + +void PlatformMessageResponseDartPort::CompleteEmpty() { + is_complete_ = true; + Dart_CObject response = { + .type = Dart_CObject_kNull, + }; + bool did_send = Dart_PostCObject(send_port_, &response); + FML_CHECK(did_send); +} + +} // namespace flutter diff --git a/lib/ui/window/platform_message_response_dart_port.h b/lib/ui/window/platform_message_response_dart_port.h new file mode 100644 index 0000000000000..5ca375437e8fa --- /dev/null +++ b/lib/ui/window/platform_message_response_dart_port.h @@ -0,0 +1,34 @@ +// 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_LIB_UI_PLATFORM_PLATFORM_MESSAGE_RESPONSE_DART_PORT_H_ +#define FLUTTER_LIB_UI_PLATFORM_PLATFORM_MESSAGE_RESPONSE_DART_PORT_H_ + +#include "flutter/fml/message_loop.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "third_party/tonic/dart_persistent_value.h" + +namespace flutter { + +class PlatformMessageResponseDartPort : public PlatformMessageResponse { + FML_FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDartPort); + + public: + // Callable on any thread. + void Complete(std::unique_ptr data) override; + void CompleteEmpty() override; + + protected: + explicit PlatformMessageResponseDartPort(Dart_Port send_port, + int64_t identifier, + const std::string& channel); + + Dart_Port send_port_; + int64_t identifier_; + const std::string channel_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_PLATFORM_PLATFORM_MESSAGE_RESPONSE_DART_PORT_H_ diff --git a/lib/web_ui/lib/platform_dispatcher.dart b/lib/web_ui/lib/platform_dispatcher.dart index 9c5bbb749d5ac..9580c04f7bf6e 100644 --- a/lib/web_ui/lib/platform_dispatcher.dart +++ b/lib/web_ui/lib/platform_dispatcher.dart @@ -49,6 +49,16 @@ abstract class PlatformDispatcher { PlatformMessageResponseCallback? callback, ); + void sendPortPlatformMessage( + String name, + ByteData? data, + int identifier, + Object port); + + int registerRootIsolate(); + + void registerBackgroundIsolate(int rootIsolateId); + PlatformMessageCallback? get onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback); diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index e195aec769295..0d534d216ccf5 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -347,6 +347,26 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { name, data, _zonedPlatformMessageResponseCallback(callback)); } + @override + void sendPortPlatformMessage( + String name, + ByteData? data, + int identifier, + Object port, + ) { + throw Exception("Isolates aren't supported in web.") + } + + @override + int registerRootIsolate() { + throw Exception("Isolates aren't supported in web.") + } + + @override + void registerBackgroundIsolate(int rootIsolateId) { + throw Exception("Isolates aren't supported in web.") + } + // TODO(ianh): Deprecate onPlatformMessage once the framework is moved over // to using channel buffers exclusively. @override diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index e12059d229e95..fbd0776fb63da 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -269,7 +269,9 @@ std::weak_ptr DartIsolate::CreateRootIsolate( static_cast*>(Dart_IsolateData(vm_isolate)); (*root_isolate_data) - ->SetPlatformConfiguration(std::move(platform_configuration)); + ->SetPlatformConfiguration( + fml::threadsafe_unique_ptr( + std::move(platform_configuration))); return (*root_isolate_data)->GetWeakIsolatePtr(); } @@ -960,6 +962,10 @@ bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, false, // is_root_isolate context))); // context + (*embedder_isolate.get()) + ->SetWeakPlatformConfiguration( + (*isolate_group_data)->GetPlatformConfiguration(1)); + // root isolate should have been created via CreateRootIsolate if (!InitializeIsolate(*embedder_isolate, isolate, error)) { return false; diff --git a/runtime/dart_isolate_group_data.cc b/runtime/dart_isolate_group_data.cc index 5850d04ea68b3..1a40712943cd8 100644 --- a/runtime/dart_isolate_group_data.cc +++ b/runtime/dart_isolate_group_data.cc @@ -64,4 +64,21 @@ void DartIsolateGroupData::SetChildIsolatePreparer( child_isolate_preparer_ = value; } +void DartIsolateGroupData::SetPlatformConfiguration( + int64_t root_isolate_id, + fml::threadsafe_unique_ptr::weak_ptr + platform_configuration) { + std::scoped_lock lock(platform_configurations_mutex_); + platform_configurations_[root_isolate_id] = platform_configuration; +} + +fml::threadsafe_unique_ptr::weak_ptr +DartIsolateGroupData::GetPlatformConfiguration(int64_t root_isolate_id) const { + std::scoped_lock lock(platform_configurations_mutex_); + auto it = platform_configurations_.find(root_isolate_id); + return it == platform_configurations_.end() + ? fml::threadsafe_unique_ptr::weak_ptr() + : it->second; +} + } // namespace flutter diff --git a/runtime/dart_isolate_group_data.h b/runtime/dart_isolate_group_data.h index 4306bca1d6b00..7c9e5ae195fea 100644 --- a/runtime/dart_isolate_group_data.h +++ b/runtime/dart_isolate_group_data.h @@ -5,12 +5,15 @@ #ifndef FLUTTER_RUNTIME_DART_ISOLATE_GROUP_DATA_H_ #define FLUTTER_RUNTIME_DART_ISOLATE_GROUP_DATA_H_ +#include #include #include #include "flutter/common/settings.h" #include "flutter/fml/closure.h" #include "flutter/fml/memory/ref_ptr.h" +#include "flutter/fml/memory/threadsafe_unique_ptr.h" +#include "flutter/lib/ui/window/platform_configuration.h" namespace flutter { @@ -25,7 +28,7 @@ using ChildIsolatePreparer = std::function; // // This object must be thread safe because the Dart VM can invoke the isolate // group cleanup callback on any thread. -class DartIsolateGroupData { +class DartIsolateGroupData : public PlatformConfigurationStorage { public: DartIsolateGroupData(const Settings& settings, fml::RefPtr isolate_snapshot, @@ -53,6 +56,16 @@ class DartIsolateGroupData { void SetChildIsolatePreparer(const ChildIsolatePreparer& value); + /// |PlatformConfigurationStorage| + void SetPlatformConfiguration( + int64_t root_isolate_id, + fml::threadsafe_unique_ptr::weak_ptr + platform_configuration) override; + + /// |PlatformConfigurationStorage| + fml::threadsafe_unique_ptr::weak_ptr + GetPlatformConfiguration(int64_t root_isolate_id) const override; + private: const Settings settings_; const fml::RefPtr isolate_snapshot_; @@ -62,6 +75,9 @@ class DartIsolateGroupData { ChildIsolatePreparer child_isolate_preparer_; const fml::closure isolate_create_callback_; const fml::closure isolate_shutdown_callback_; + std::map::weak_ptr> + platform_configurations_; + mutable std::mutex platform_configurations_mutex_; FML_DISALLOW_COPY_AND_ASSIGN(DartIsolateGroupData); }; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 9a0e5d03e4883..975cf657e307d 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -460,6 +460,7 @@ void Engine::UpdateSemantics(SemanticsNodeUpdates update, void Engine::HandlePlatformMessage(std::unique_ptr message) { if (message->channel() == kAssetChannel) { + FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); HandleAssetPlatformMessage(std::move(message)); } else { delegate_.OnEngineHandlePlatformMessage(std::move(message)); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index e28a0391a3552..bf4e4cb996725 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1211,10 +1211,15 @@ void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update, // |Engine::Delegate| void Shell::OnEngineHandlePlatformMessage( std::unique_ptr message) { + /// Called from any isolate's thread. This is safe because the only instance + /// variables accessed here are set once at startup, except + /// `route_messages_through_platform_thread_` which if misread is not a + /// logical error. `UIDartState` has a lock that makes sure that calling this + /// doesn't happen while the `Shell` is being destructed. FML_DCHECK(is_setup_); - FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (message->channel() == kSkiaChannel) { + FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); HandleEngineSkiaMessage(std::move(message)); return; } diff --git a/shell/platform/darwin/ios/platform_message_handler_ios.mm b/shell/platform/darwin/ios/platform_message_handler_ios.mm index 7f6efcd63cfc0..a3d9c75456bb9 100644 --- a/shell/platform/darwin/ios/platform_message_handler_ios.mm +++ b/shell/platform/darwin/ios/platform_message_handler_ios.mm @@ -47,10 +47,13 @@ - (void)dispatch:(dispatch_block_t)block { : task_runners_(task_runners) {} void PlatformMessageHandlerIos::HandlePlatformMessage(std::unique_ptr message) { - FML_CHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + // This can be called from any isolate's thread. fml::RefPtr completer = message->response(); HandlerInfo handler_info; { + // TODO(gaaclarke): This mutex is a bottleneck for multiple isolates sending + // messages at the same time. This could be potentially changed to a + // read-write lock. std::lock_guard lock(message_handlers_mutex_); auto it = message_handlers_.find(message->channel()); if (it != message_handlers_.end()) {