diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index fbb2dd514e5..1c5a726067e 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -16,6 +16,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/closure.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/task_queue_id.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/unique_fd.h" @@ -70,8 +71,10 @@ class FrameTiming { }; using TaskObserverAdd = - std::function; -using TaskObserverRemove = std::function; + std::function; +using TaskObserverRemove = + std::function; using UnhandledExceptionCallback = std::function; @@ -359,9 +362,20 @@ struct Settings { /// This is used by the runOnPlatformThread API. bool enable_platform_isolates = false; - // If true, the UI thread is the platform thread on supported - // platforms. - bool merged_platform_ui_thread = true; + enum class MergedPlatformUIThread { + // Use separate threads for the UI and platform task runners. + kDisabled, + // Use the platform thread for both the UI and platform task runners. + kEnabled, + // Start the engine on a separate UI thread and then move the UI task + // runner to the platform thread after the engine is initialized. + // This can improve app launch latency by allowing other work to run on + // the platform thread during engine startup. + kMergeAfterLaunch + }; + + MergedPlatformUIThread merged_platform_ui_thread = + MergedPlatformUIThread::kEnabled; }; } // namespace flutter diff --git a/engine/src/flutter/fml/platform/fuchsia/task_observers.cc b/engine/src/flutter/fml/platform/fuchsia/task_observers.cc index d3a9ea1ede6..beede2e8e04 100644 --- a/engine/src/flutter/fml/platform/fuchsia/task_observers.cc +++ b/engine/src/flutter/fml/platform/fuchsia/task_observers.cc @@ -16,14 +16,16 @@ void ExecuteAfterTaskObservers() { } } -void CurrentMessageLoopAddAfterTaskObserver(intptr_t key, - fit::closure observer) { +fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key, + fit::closure observer) { if (observer) { tTaskObservers[key] = std::move(observer); } + return fml::TaskQueueId::Invalid(); } -void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key) { +void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id, + intptr_t key) { tTaskObservers.erase(key); } diff --git a/engine/src/flutter/fml/platform/fuchsia/task_observers.h b/engine/src/flutter/fml/platform/fuchsia/task_observers.h index 21596ad299f..b634aef786a 100644 --- a/engine/src/flutter/fml/platform/fuchsia/task_observers.h +++ b/engine/src/flutter/fml/platform/fuchsia/task_observers.h @@ -7,6 +7,8 @@ #include +#include "flutter/fml/task_queue_id.h" + namespace fml { // Executes all closures that were registered via @@ -30,10 +32,11 @@ namespace fml { // somehow. void ExecuteAfterTaskObservers(); -void CurrentMessageLoopAddAfterTaskObserver(intptr_t key, - fit::closure observer); +fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key, + fit::closure observer); -void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key); +void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id, + intptr_t key); } // namespace fml diff --git a/engine/src/flutter/fml/task_queue_id.h b/engine/src/flutter/fml/task_queue_id.h index 12f4206c7c7..4efb6e486a2 100644 --- a/engine/src/flutter/fml/task_queue_id.h +++ b/engine/src/flutter/fml/task_queue_id.h @@ -25,6 +25,8 @@ class TaskQueueId { /// Intializes a task queue with the given value as it's ID. explicit TaskQueueId(size_t value) : value_(value) {} + static TaskQueueId Invalid() { return TaskQueueId(kInvalid); } + operator size_t() const { // NOLINT(google-explicit-constructor) return value_; } diff --git a/engine/src/flutter/lib/ui/painting/image_decoder.cc b/engine/src/flutter/lib/ui/painting/image_decoder.cc index db2c8dd461c..da8a84252c1 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder.cc +++ b/engine/src/flutter/lib/ui/painting/image_decoder.cc @@ -55,7 +55,7 @@ ImageDecoder::ImageDecoder( ImageDecoder::~ImageDecoder() = default; -fml::WeakPtr ImageDecoder::GetWeakPtr() const { +fml::TaskRunnerAffineWeakPtr ImageDecoder::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } diff --git a/engine/src/flutter/lib/ui/painting/image_decoder.h b/engine/src/flutter/lib/ui/painting/image_decoder.h index ae87dbf092a..78fdbe5fb1b 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder.h +++ b/engine/src/flutter/lib/ui/painting/image_decoder.h @@ -44,7 +44,7 @@ class ImageDecoder { uint32_t target_height, const ImageResult& result) = 0; - fml::WeakPtr GetWeakPtr() const; + fml::TaskRunnerAffineWeakPtr GetWeakPtr() const; protected: TaskRunners runners_; @@ -57,7 +57,7 @@ class ImageDecoder { fml::WeakPtr io_manager); private: - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(ImageDecoder); }; diff --git a/engine/src/flutter/lib/ui/painting/image_generator_registry.cc b/engine/src/flutter/lib/ui/painting/image_generator_registry.cc index ca5d83a36d3..e5a8b9ac621 100644 --- a/engine/src/flutter/lib/ui/painting/image_generator_registry.cc +++ b/engine/src/flutter/lib/ui/painting/image_generator_registry.cc @@ -79,8 +79,8 @@ ImageGeneratorRegistry::CreateCompatibleGenerator(const sk_sp& buffer) { return nullptr; } -fml::WeakPtr ImageGeneratorRegistry::GetWeakPtr() - const { +fml::TaskRunnerAffineWeakPtr +ImageGeneratorRegistry::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } diff --git a/engine/src/flutter/lib/ui/painting/image_generator_registry.h b/engine/src/flutter/lib/ui/painting/image_generator_registry.h index 09227d7c925..ea78020b93c 100644 --- a/engine/src/flutter/lib/ui/painting/image_generator_registry.h +++ b/engine/src/flutter/lib/ui/painting/image_generator_registry.h @@ -58,7 +58,7 @@ class ImageGeneratorRegistry { std::shared_ptr CreateCompatibleGenerator( const sk_sp& buffer); - fml::WeakPtr GetWeakPtr() const; + fml::TaskRunnerAffineWeakPtr GetWeakPtr() const; private: struct PrioritizedFactory { @@ -85,7 +85,7 @@ class ImageGeneratorRegistry { using FactorySet = std::set; FactorySet image_generator_factories_; size_t nonce_; - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; }; } // namespace flutter diff --git a/engine/src/flutter/lib/ui/ui_dart_state.cc b/engine/src/flutter/lib/ui/ui_dart_state.cc index 8a7685dd5f4..f0483bd9797 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.cc +++ b/engine/src/flutter/lib/ui/ui_dart_state.cc @@ -24,8 +24,9 @@ UIDartState::Context::Context( fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr unref_queue, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, std::string advisory_script_uri, std::string advisory_script_entrypoint, bool deterministic_rendering_enabled, @@ -58,6 +59,7 @@ UIDartState::UIDartState( const UIDartState::Context& context) : add_callback_(std::move(add_callback)), remove_callback_(std::move(remove_callback)), + callback_queue_id_(fml::TaskQueueId::kInvalid), logger_prefix_(std::move(logger_prefix)), is_root_isolate_(is_root_isolate), unhandled_exception_callback_(std::move(unhandled_exception_callback)), @@ -178,10 +180,12 @@ void UIDartState::AddOrRemoveTaskObserver(bool add) { } FML_DCHECK(add_callback_ && remove_callback_); if (add) { - add_callback_(reinterpret_cast(this), - [this]() { this->FlushMicrotasksNow(); }); + callback_queue_id_ = + add_callback_(reinterpret_cast(this), + [this]() { this->FlushMicrotasksNow(); }); } else { - remove_callback_(reinterpret_cast(this)); + remove_callback_(callback_queue_id_, reinterpret_cast(this)); + callback_queue_id_ = fml::TaskQueueId::Invalid(); } } @@ -190,12 +194,13 @@ UIDartState::GetSnapshotDelegate() const { return context_.snapshot_delegate; } -fml::WeakPtr UIDartState::GetImageDecoder() const { +fml::TaskRunnerAffineWeakPtr UIDartState::GetImageDecoder() + const { return context_.image_decoder; } -fml::WeakPtr UIDartState::GetImageGeneratorRegistry() - const { +fml::TaskRunnerAffineWeakPtr +UIDartState::GetImageGeneratorRegistry() const { return context_.image_generator_registry; } diff --git a/engine/src/flutter/lib/ui/ui_dart_state.h b/engine/src/flutter/lib/ui/ui_dart_state.h index 9ab94592081..5b60b3de77d 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.h +++ b/engine/src/flutter/lib/ui/ui_dart_state.h @@ -48,8 +48,9 @@ class UIDartState : public tonic::DartState { fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr unref_queue, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, std::string advisory_script_uri, std::string advisory_script_entrypoint, bool deterministic_rendering_enabled, @@ -76,12 +77,13 @@ class UIDartState : public tonic::DartState { fml::RefPtr unref_queue; /// The image decoder. - fml::WeakPtr image_decoder; + fml::TaskRunnerAffineWeakPtr image_decoder; /// Cascading registry of image generator builders. Given compressed image /// bytes as input, this is used to find and create image generators, which /// can then be used for image decoding. - fml::WeakPtr image_generator_registry; + fml::TaskRunnerAffineWeakPtr + image_generator_registry; /// The advisory script URI (only used for debugging). This does not affect /// the code being run in the isolate in any way. @@ -144,9 +146,10 @@ class UIDartState : public tonic::DartState { fml::TaskRunnerAffineWeakPtr GetSnapshotDelegate() const; - fml::WeakPtr GetImageDecoder() const; + fml::TaskRunnerAffineWeakPtr GetImageDecoder() const; - fml::WeakPtr GetImageGeneratorRegistry() const; + fml::TaskRunnerAffineWeakPtr + GetImageGeneratorRegistry() const; std::shared_ptr GetIsolateNameServer() const; @@ -205,6 +208,7 @@ class UIDartState : public tonic::DartState { const TaskObserverAdd add_callback_; const TaskObserverRemove remove_callback_; + fml::TaskQueueId callback_queue_id_; const std::string logger_prefix_; Dart_Port main_port_ = ILLEGAL_PORT; const bool is_root_isolate_; diff --git a/engine/src/flutter/runtime/dart_isolate.cc b/engine/src/flutter/runtime/dart_isolate.cc index 84e0e9a6894..2dc89b8e611 100644 --- a/engine/src/flutter/runtime/dart_isolate.cc +++ b/engine/src/flutter/runtime/dart_isolate.cc @@ -155,7 +155,10 @@ std::weak_ptr DartIsolate::CreateRunningRootIsolate( { tonic::DartState::Scope scope(isolate.get()); - Dart_SetCurrentThreadOwnsIsolate(); + if (settings.merged_platform_ui_thread != + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + Dart_SetCurrentThreadOwnsIsolate(); + } if (settings.root_isolate_create_callback) { // Isolate callbacks always occur in isolate scope and before user code @@ -349,6 +352,7 @@ Dart_Isolate DartIsolate::CreatePlatformIsolate(Dart_Handle entry_point, } old_task_observer_add(key, callback); }); + return platform_task_runner->GetTaskQueueId(); }; UIDartState::Context context(task_runners); @@ -1373,6 +1377,11 @@ std::weak_ptr DartIsolate::GetWeakIsolatePtr() { return std::static_pointer_cast(shared_from_this()); } +void DartIsolate::SetOwnerToCurrentThread() { + tonic::DartIsolateScope isolate_scope(isolate()); + Dart_SetCurrentThreadOwnsIsolate(); +} + void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) { shutdown_callbacks_.emplace_back(std::make_unique(closure)); } diff --git a/engine/src/flutter/runtime/dart_isolate.h b/engine/src/flutter/runtime/dart_isolate.h index 4b2116ed70a..3ce5b963fbd 100644 --- a/engine/src/flutter/runtime/dart_isolate.h +++ b/engine/src/flutter/runtime/dart_isolate.h @@ -411,6 +411,10 @@ class DartIsolate : public UIDartState { static Dart_Handle LoadLibraryFromKernel( const std::shared_ptr& mapping); + // Calls a Dart API that sets the isolate's owner thread to the current + // thread. + void SetOwnerToCurrentThread(); + private: friend class IsolateConfiguration; class AutoFireClosure { diff --git a/engine/src/flutter/runtime/runtime_controller.cc b/engine/src/flutter/runtime/runtime_controller.cc index def621fe3fb..dcdfa69ef88 100644 --- a/engine/src/flutter/runtime/runtime_controller.cc +++ b/engine/src/flutter/runtime/runtime_controller.cc @@ -56,8 +56,9 @@ std::unique_ptr RuntimeController::Spawn( const fml::closure& p_isolate_shutdown_callback, const std::shared_ptr& p_persistent_isolate_data, fml::WeakPtr io_manager, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, fml::TaskRunnerAffineWeakPtr snapshot_delegate) const { UIDartState::Context spawned_context{context_.task_runners, std::move(snapshot_delegate), @@ -676,6 +677,13 @@ void RuntimeController::ShutdownPlatformIsolates() { platform_isolate_manager_->ShutdownPlatformIsolates(); } +void RuntimeController::SetRootIsolateOwnerToCurrentThread() { + std::shared_ptr root_isolate = root_isolate_.lock(); + if (root_isolate) { + root_isolate->SetOwnerToCurrentThread(); + } +} + RuntimeController::Locale::Locale(std::string language_code_, std::string country_code_, std::string script_code_, diff --git a/engine/src/flutter/runtime/runtime_controller.h b/engine/src/flutter/runtime/runtime_controller.h index 5b11cf2b819..e5dbdfdcda5 100644 --- a/engine/src/flutter/runtime/runtime_controller.h +++ b/engine/src/flutter/runtime/runtime_controller.h @@ -121,8 +121,9 @@ class RuntimeController : public PlatformConfigurationClient, const fml::closure& isolate_shutdown_callback, const std::shared_ptr& persistent_isolate_data, fml::WeakPtr io_manager, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, fml::TaskRunnerAffineWeakPtr snapshot_delegate) const; // |PlatformConfigurationClient| @@ -678,6 +679,8 @@ class RuntimeController : public PlatformConfigurationClient, return platform_isolate_manager_; } + void SetRootIsolateOwnerToCurrentThread(); + //-------------------------------------------------------------------------- /// @brief Shuts down all registered platform isolates. Must be called /// from the platform thread. diff --git a/engine/src/flutter/shell/common/animator.h b/engine/src/flutter/shell/common/animator.h index d559945358b..e245b1e02bd 100644 --- a/engine/src/flutter/shell/common/animator.h +++ b/engine/src/flutter/shell/common/animator.h @@ -148,7 +148,7 @@ class Animator final { std::deque trace_flow_ids_; bool has_rendered_ = false; - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; friend class testing::ShellTest; diff --git a/engine/src/flutter/shell/common/engine.cc b/engine/src/flutter/shell/common/engine.cc index be7cddf05c8..c1d6795af22 100644 --- a/engine/src/flutter/shell/common/engine.cc +++ b/engine/src/flutter/shell/common/engine.cc @@ -148,7 +148,7 @@ std::unique_ptr Engine::Spawn( Engine::~Engine() = default; -fml::WeakPtr Engine::GetWeakPtr() const { +fml::TaskRunnerAffineWeakPtr Engine::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } @@ -161,11 +161,12 @@ std::shared_ptr Engine::GetAssetManager() { return asset_manager_; } -fml::WeakPtr Engine::GetImageDecoderWeakPtr() { +fml::TaskRunnerAffineWeakPtr Engine::GetImageDecoderWeakPtr() { return image_decoder_->GetWeakPtr(); } -fml::WeakPtr Engine::GetImageGeneratorRegistry() { +fml::TaskRunnerAffineWeakPtr +Engine::GetImageGeneratorRegistry() { return image_generator_registry_.GetWeakPtr(); } @@ -239,6 +240,19 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { } }; + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Queue a task to the UI task runner that sets the owner of the root + // isolate. This task runs after the thread merge and will therefore be + // executed on the platform thread. The task will run before any tasks + // queued by LaunchRootIsolate that execute the app's Dart code. + task_runners_.GetUITaskRunner()->PostTask([engine = GetWeakPtr()]() { + if (engine) { + engine->runtime_controller_->SetRootIsolateOwnerToCurrentThread(); + } + }); + } + if (!runtime_controller_->LaunchRootIsolate( settings_, // root_isolate_create_callback, // @@ -260,6 +274,18 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { HandlePlatformMessage(std::move(service_id_message)); } + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Move the UI task runner to the platform thread. + bool success = fml::MessageLoopTaskQueues::GetInstance()->Merge( + task_runners_.GetPlatformTaskRunner()->GetTaskQueueId(), + task_runners_.GetUITaskRunner()->GetTaskQueueId()); + if (!success) { + FML_LOG(ERROR) + << "Unable to move the UI task runner to the platform thread"; + } + } + return Engine::RunStatus::Success; } diff --git a/engine/src/flutter/shell/common/engine.h b/engine/src/flutter/shell/common/engine.h index 8d911741931..7226fbf4b99 100644 --- a/engine/src/flutter/shell/common/engine.h +++ b/engine/src/flutter/shell/common/engine.h @@ -441,7 +441,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// @return The pointer to this instance of the engine. The engine may /// only be accessed safely on the UI task runner. /// - fml::WeakPtr GetWeakPtr() const; + fml::TaskRunnerAffineWeakPtr GetWeakPtr() const; //---------------------------------------------------------------------------- /// @brief Moves the root isolate to the `DartIsolate::Phase::Running` @@ -877,7 +877,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { std::shared_ptr GetAssetManager() override; // Return the weak_ptr of ImageDecoder. - fml::WeakPtr GetImageDecoderWeakPtr(); + fml::TaskRunnerAffineWeakPtr GetImageDecoderWeakPtr(); //---------------------------------------------------------------------------- /// @brief Get the `ImageGeneratorRegistry` associated with the current @@ -885,7 +885,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// /// @return The engine's `ImageGeneratorRegistry`. /// - fml::WeakPtr GetImageGeneratorRegistry(); + fml::TaskRunnerAffineWeakPtr + GetImageGeneratorRegistry(); // |PointerDataDispatcher::Delegate| void DoDispatchPacket(std::unique_ptr packet, @@ -1084,7 +1085,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { const std::unique_ptr image_decoder_; ImageGeneratorRegistry image_generator_registry_; TaskRunners task_runners_; - fml::WeakPtrFactory weak_factory_; // Must be the last member. + fml::TaskRunnerAffineWeakPtrFactory + weak_factory_; // Must be the last member. FML_DISALLOW_COPY_AND_ASSIGN(Engine); }; diff --git a/engine/src/flutter/shell/common/pointer_data_dispatcher.h b/engine/src/flutter/shell/common/pointer_data_dispatcher.h index b749bfeee99..88c0167fc88 100644 --- a/engine/src/flutter/shell/common/pointer_data_dispatcher.h +++ b/engine/src/flutter/shell/common/pointer_data_dispatcher.h @@ -159,7 +159,8 @@ class SmoothPointerDataDispatcher : public DefaultPointerDataDispatcher { bool is_pointer_data_in_progress_ = false; // WeakPtrFactory must be the last member. - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory + weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(SmoothPointerDataDispatcher); }; diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index f70991ca006..e348f690ed4 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -567,6 +567,19 @@ Shell::~Shell() { platform_latch.Signal(); })); platform_latch.Wait(); + + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Move the UI task runner back to its original thread to enable shutdown of + // that thread. + auto task_queues = fml::MessageLoopTaskQueues::GetInstance(); + auto platform_queue_id = + task_runners_.GetPlatformTaskRunner()->GetTaskQueueId(); + auto ui_queue_id = task_runners_.GetUITaskRunner()->GetTaskQueueId(); + if (task_queues->Owns(platform_queue_id, ui_queue_id)) { + task_queues->Unmerge(platform_queue_id, ui_queue_id); + } + } } std::unique_ptr Shell::Spawn( @@ -575,6 +588,16 @@ std::unique_ptr Shell::Spawn( const CreateCallback& on_create_platform_view, const CreateCallback& on_create_rasterizer) const { FML_DCHECK(task_runners_.IsValid()); + + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Spawning engines that share the same task runners can result in + // deadlocks when the UI task runner is moved to the platform thread. + FML_LOG(ERROR) << "MergedPlatformUIThread::kMergeAfterLaunch does not " + "support spawning"; + return nullptr; + } + // It's safe to store this value since it is set on the platform thread. bool is_gpu_disabled = false; GetIsGpuDisabledSyncSwitch()->Execute( @@ -811,7 +834,7 @@ fml::TaskRunnerAffineWeakPtr Shell::GetRasterizer() const { return weak_rasterizer_; } -fml::WeakPtr Shell::GetEngine() { +fml::TaskRunnerAffineWeakPtr Shell::GetEngine() { FML_DCHECK(is_set_up_); return weak_engine_; } @@ -1079,12 +1102,12 @@ void Shell::OnPlatformViewDispatchPlatformMessage( // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) fml::TaskRunner::RunNowAndFlushMessages( task_runners_.GetUITaskRunner(), - fml::MakeCopyable([engine = engine_->GetWeakPtr(), - message = std::move(message)]() mutable { - if (engine) { - engine->DispatchPlatformMessage(std::move(message)); - } - })); + fml::MakeCopyable( + [engine = weak_engine_, message = std::move(message)]() mutable { + if (engine) { + engine->DispatchPlatformMessage(std::move(message)); + } + })); } // |PlatformView::Delegate| diff --git a/engine/src/flutter/shell/common/shell.h b/engine/src/flutter/shell/common/shell.h index a5305e7abfd..0cd7ef54992 100644 --- a/engine/src/flutter/shell/common/shell.h +++ b/engine/src/flutter/shell/common/shell.h @@ -260,7 +260,7 @@ class Shell final : public PlatformView::Delegate, /// /// @return A weak pointer to the engine. /// - fml::WeakPtr GetEngine(); + fml::TaskRunnerAffineWeakPtr GetEngine(); //---------------------------------------------------------------------------- /// @brief Platform views may only be accessed on the platform task @@ -470,7 +470,8 @@ class Shell final : public PlatformView::Delegate, std::shared_ptr platform_message_handler_; std::atomic route_messages_through_platform_thread_ = false; - fml::WeakPtr weak_engine_; // to be shared across threads + fml::TaskRunnerAffineWeakPtr + weak_engine_; // to be shared across threads fml::TaskRunnerAffineWeakPtr weak_rasterizer_; // to be shared across threads fml::WeakPtr diff --git a/engine/src/flutter/shell/common/shell_benchmarks.cc b/engine/src/flutter/shell/common/shell_benchmarks.cc index a1bba720fe9..151aebd5045 100644 --- a/engine/src/flutter/shell/common/shell_benchmarks.cc +++ b/engine/src/flutter/shell/common/shell_benchmarks.cc @@ -25,8 +25,10 @@ static void StartupAndShutdownShell(benchmark::State& state, { benchmarking::ScopedPauseTiming pause(state, !measure_startup); Settings settings = {}; - settings.task_observer_add = [](intptr_t, const fml::closure&) {}; - settings.task_observer_remove = [](intptr_t) {}; + settings.task_observer_add = [](intptr_t, const fml::closure&) { + return fml::TaskQueueId::Invalid(); + }; + settings.task_observer_remove = [](fml::TaskQueueId, intptr_t) {}; if (DartVM::IsRunningPrecompiledCode()) { aot_symbols = testing::LoadELFSymbolFromFixturesIfNeccessary( diff --git a/engine/src/flutter/shell/common/shell_test.cc b/engine/src/flutter/shell/common/shell_test.cc index 7dc7f80369c..24ce62e7a7b 100644 --- a/engine/src/flutter/shell/common/shell_test.cc +++ b/engine/src/flutter/shell/common/shell_test.cc @@ -226,7 +226,8 @@ void ShellTest::PumpOneFrame(Shell* shell, FrameContent frame_content) { // tree pipeline nonempty. Without either of this, the layer tree below // won't be rasterized. fml::AutoResetWaitableEvent latch; - fml::WeakPtr runtime_delegate = shell->weak_engine_; + fml::TaskRunnerAffineWeakPtr runtime_delegate = + shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, engine = shell->weak_engine_, &frame_content, runtime_delegate]() { @@ -341,10 +342,14 @@ Settings ShellTest::CreateSettingsForFixture() { Settings settings; settings.leak_vm = false; settings.task_observer_add = [](intptr_t key, const fml::closure& handler) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, handler); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + handler); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); }; settings.isolate_create_callback = [this]() { native_resolver_->SetNativeResolverForIsolate(); diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index 2bbf216bd0c..8cdcbc5e0c7 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -5072,6 +5072,47 @@ TEST_F(ShellTest, ProvidesNullEngineId) { DestroyShell(std::move(shell), task_runners); } +TEST_F(ShellTest, MergeUIAndPlatformThreadsAfterLaunch) { + Settings settings = CreateSettingsForFixture(); + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kMergeAfterLaunch; + ThreadHost thread_host(ThreadHost::ThreadHostConfig( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster | + ThreadHost::Type::kIo | ThreadHost::Type::kUi)); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + + std::unique_ptr shell = CreateShell(settings, task_runners); + ASSERT_TRUE(shell); + + ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread( + task_runners.GetUITaskRunner()->GetTaskQueueId(), + task_runners.GetPlatformTaskRunner()->GetTaskQueueId())); + + fml::AutoResetWaitableEvent latch; + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) { + ASSERT_TRUE( + task_runners.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + latch.Signal(); + })); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("mainNotifyNative"); + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + + ASSERT_TRUE(fml::TaskRunnerChecker::RunsOnTheSameThread( + task_runners.GetUITaskRunner()->GetTaskQueueId(), + task_runners.GetPlatformTaskRunner()->GetTaskQueueId())); + + DestroyShell(std::move(shell), task_runners); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index fc2b51fa4f1..a653b57f0e5 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -529,8 +529,26 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.enable_surface_control = command_line.HasOption( FlagForSwitch(Switch::EnableAndroidSurfaceControl)); - settings.merged_platform_ui_thread = !command_line.HasOption( - FlagForSwitch(Switch::DisableMergedPlatformUIThread)); + if (command_line.HasOption( + FlagForSwitch(Switch::DisableMergedPlatformUIThread))) { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kDisabled; + } else if (command_line.HasOption( + FlagForSwitch(Switch::MergedPlatformUIThread))) { + std::string merged_platform_ui; + command_line.GetOptionValue(FlagForSwitch(Switch::MergedPlatformUIThread), + &merged_platform_ui); + if (merged_platform_ui == "enabled") { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kEnabled; + } else if (merged_platform_ui == "disabled") { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kDisabled; + } else if (merged_platform_ui == "mergeAfterLaunch") { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kMergeAfterLaunch; + } + } settings.enable_flutter_gpu = command_line.HasOption(FlagForSwitch(Switch::EnableFlutterGPU)); diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 7a2d04e5e03..238d890a962 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -294,6 +294,11 @@ DEF_SWITCH(EnableEmbedderAPI, DEF_SWITCH(EnablePlatformIsolates, "enable-platform-isolates", "Enable support for isolates that run on the platform thread.") +DEF_SWITCH(MergedPlatformUIThread, + "merged-platform-ui-thread", + "Sets whether the ui thread and platform thread should be merged.") +// This is a legacy flag that has been superseded by merged-platform-ui-thread. +// TODO(163064): remove this when users have been migrated. DEF_SWITCH(DisableMergedPlatformUIThread, "no-enable-merged-platform-ui-thread", "Merge the ui thread and platform thread.") diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder.cc b/engine/src/flutter/shell/platform/android/android_shell_holder.cc index 58a0eb0707d..9bf3ddc1fd1 100644 --- a/engine/src/flutter/shell/platform/android/android_shell_holder.cc +++ b/engine/src/flutter/shell/platform/android/android_shell_holder.cc @@ -90,7 +90,8 @@ AndroidShellHolder::AndroidShellHolder( auto thread_label = std::to_string(thread_host_count++); auto mask = ThreadHost::Type::kRaster | ThreadHost::Type::kIo; - if (!settings.merged_platform_ui_thread) { + if (settings.merged_platform_ui_thread != + Settings::MergedPlatformUIThread::kEnabled) { mask |= ThreadHost::Type::kUi; } @@ -139,7 +140,8 @@ AndroidShellHolder::AndroidShellHolder( fml::RefPtr platform_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); raster_runner = thread_host_->raster_thread->GetTaskRunner(); - if (settings.merged_platform_ui_thread) { + if (settings.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kEnabled) { ui_runner = platform_runner; } else { ui_runner = thread_host_->ui_thread->GetTaskRunner(); diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc b/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc index 1b324ffbb63..2b4a5e67e0e 100644 --- a/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc +++ b/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc @@ -201,7 +201,8 @@ TEST(AndroidShellHolder, CreateWithMergedPlatformAndUIThread) { TEST(AndroidShellHolder, CreateWithUnMergedPlatformAndUIThread) { Settings settings; - settings.merged_platform_ui_thread = false; + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kDisabled; auto jni = std::make_shared(); auto holder = std::make_unique( settings, jni, AndroidRenderingAPI::kImpellerOpenGLES); diff --git a/engine/src/flutter/shell/platform/android/flutter_main.cc b/engine/src/flutter/shell/platform/android/flutter_main.cc index bf97261032c..aac7f772c78 100644 --- a/engine/src/flutter/shell/platform/android/flutter_main.cc +++ b/engine/src/flutter/shell/platform/android/flutter_main.cc @@ -147,11 +147,15 @@ void FlutterMain::Init(JNIEnv* env, } settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + callback); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); }; settings.log_message_callback = [](const std::string& tag, diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index 249309098e0..65fa2e55baf 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -368,10 +368,8 @@ public class FlutterLoader { if (metaData.getBoolean(IMPELLER_VULKAN_GPU_TRACING_DATA_KEY, false)) { shellArgs.add("--enable-vulkan-gpu-tracing"); } - if (metaData.containsKey(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY)) { - if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY)) { - shellArgs.add("--no-enable-merged-platform-ui-thread"); - } + if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY, false)) { + shellArgs.add("--merged-platform-ui-thread=disabled"); } if (metaData.getBoolean(ENABLE_FLUTTER_GPU, false)) { shellArgs.add("--enable-flutter-gpu"); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 183ff85c78e..bcfd97158a8 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -64,11 +64,13 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p auto settings = flutter::SettingsFromCommandLine(command_line); settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, callback); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, key); }; settings.log_message_callback = [](const std::string& tag, const std::string& message) { @@ -209,7 +211,9 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p NSNumber* enableMergedPlatformUIThread = [mainBundle objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"]; if (enableMergedPlatformUIThread != nil) { - settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue; + settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue + ? flutter::Settings::MergedPlatformUIThread::kEnabled + : flutter::Settings::MergedPlatformUIThread::kDisabled; } NSNumber* enableFlutterGPU = [mainBundle objectForInfoDictionaryKey:@"FLTEnableFlutterGPU"]; diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 6c3e893c89e..47eaff91a33 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -752,7 +752,7 @@ static flutter::ThreadHost MakeThreadHost(NSString* thread_label, fml::MessageLoop::EnsureInitializedForCurrentThread(); uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo; - if (!settings.merged_platform_ui_thread) { + if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) { threadHostType |= flutter::ThreadHost::Type::kUi; } @@ -839,7 +839,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS [](flutter::Shell& shell) { return std::make_unique(shell); }; fml::RefPtr ui_runner; - if (settings.enable_impeller && settings.merged_platform_ui_thread) { + if (settings.enable_impeller && + settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) { ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); } else { ui_runner = _threadHost->ui_thread->GetTaskRunner(); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index 3770bf8c6a5..6f2249c9f88 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -528,7 +528,7 @@ FLUTTER_ASSERT_ARC - (void)testCanUnMergePlatformAndUIThread { #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR auto settings = FLTDefaultSettingsForBundle(); - settings.merged_platform_ui_thread = false; + settings.merged_platform_ui_thread = flutter::Settings::MergedPlatformUIThread::kDisabled; FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings]; FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; [engine run]; diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index e5f7c4353e2..6551c2e3609 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -2389,12 +2389,18 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, settings.task_observer_add = [has_ui_thread_message_loop]( intptr_t key, const fml::closure& callback) { if (has_ui_thread_message_loop) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + callback); + return queue_id; + } else { + return fml::TaskQueueId::Invalid(); } }; - settings.task_observer_remove = [has_ui_thread_message_loop](intptr_t key) { - if (has_ui_thread_message_loop) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + if (queue_id.is_valid()) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); } }; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc index 5df9b1b0361..6d6b30d7654 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc @@ -469,8 +469,9 @@ ComponentV2::ComponentV2( std::bind(&fml::CurrentMessageLoopAddAfterTaskObserver, std::placeholders::_1, std::placeholders::_2); - settings_.task_observer_remove = std::bind( - &fml::CurrentMessageLoopRemoveAfterTaskObserver, std::placeholders::_1); + settings_.task_observer_remove = + std::bind(&fml::CurrentMessageLoopRemoveAfterTaskObserver, + std::placeholders::_1, std::placeholders::_2); settings_.log_message_callback = [](const std::string& tag, const std::string& message) { diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc index 95bdeb52be3..04861d55827 100644 --- a/engine/src/flutter/shell/testing/tester_main.cc +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -666,11 +666,15 @@ int main(int argc, char* argv[]) { }; settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + callback); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); }; settings.unhandled_exception_callback = [](const std::string& error, diff --git a/engine/src/flutter/testing/dart_fixture.cc b/engine/src/flutter/testing/dart_fixture.cc index 7c765dcb105..4ccc4baefc3 100644 --- a/engine/src/flutter/testing/dart_fixture.cc +++ b/engine/src/flutter/testing/dart_fixture.cc @@ -30,8 +30,10 @@ DartFixture::DartFixture(std::string kernel_filename, Settings DartFixture::CreateSettingsForFixture() { Settings settings; settings.leak_vm = false; - settings.task_observer_add = [](intptr_t, const fml::closure&) {}; - settings.task_observer_remove = [](intptr_t) {}; + settings.task_observer_add = [](intptr_t, const fml::closure&) { + return fml::TaskQueueId::Invalid(); + }; + settings.task_observer_remove = [](fml::TaskQueueId, intptr_t) {}; settings.isolate_create_callback = [this]() { native_resolver_->SetNativeResolverForIsolate(); };