From c53fdbdf24daba4bc81ccc6d274ce13d7d34d05e Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 17 Apr 2025 21:40:08 +0000 Subject: [PATCH] Reduce app startup latency by initializing the engine on a separate thread (#166918) If settings.merged_platform_ui_thread is set to kMergeAfterLaunch, then the engine will be started on the UI thread. After engine setup completes and the Dart isolate is loaded, the UI task runner will be merged into the platform thread and all future Dart execution will run on the platform thread. This makes it possible for other work to run on the platform thread while the engine starts. See https://github.com/flutter/flutter/issues/163064 --- engine/src/flutter/common/settings.h | 24 ++++++++--- .../fml/platform/fuchsia/task_observers.cc | 8 ++-- .../fml/platform/fuchsia/task_observers.h | 9 ++-- engine/src/flutter/fml/task_queue_id.h | 2 + .../flutter/lib/ui/painting/image_decoder.cc | 2 +- .../flutter/lib/ui/painting/image_decoder.h | 4 +- .../ui/painting/image_generator_registry.cc | 4 +- .../ui/painting/image_generator_registry.h | 4 +- engine/src/flutter/lib/ui/ui_dart_state.cc | 21 ++++++---- engine/src/flutter/lib/ui/ui_dart_state.h | 16 +++++--- engine/src/flutter/runtime/dart_isolate.cc | 11 ++++- engine/src/flutter/runtime/dart_isolate.h | 4 ++ .../src/flutter/runtime/runtime_controller.cc | 12 +++++- .../src/flutter/runtime/runtime_controller.h | 7 +++- engine/src/flutter/shell/common/animator.h | 2 +- engine/src/flutter/shell/common/engine.cc | 32 +++++++++++++-- engine/src/flutter/shell/common/engine.h | 10 +++-- .../shell/common/pointer_data_dispatcher.h | 3 +- engine/src/flutter/shell/common/shell.cc | 37 +++++++++++++---- engine/src/flutter/shell/common/shell.h | 5 ++- .../flutter/shell/common/shell_benchmarks.cc | 6 ++- engine/src/flutter/shell/common/shell_test.cc | 13 ++++-- .../flutter/shell/common/shell_unittests.cc | 41 +++++++++++++++++++ engine/src/flutter/shell/common/switches.cc | 22 +++++++++- engine/src/flutter/shell/common/switches.h | 5 +++ .../platform/android/android_shell_holder.cc | 6 ++- .../android/android_shell_holder_unittests.cc | 3 +- .../shell/platform/android/flutter_main.cc | 10 +++-- .../engine/loader/FlutterLoader.java | 6 +-- .../framework/Source/FlutterDartProject.mm | 12 ++++-- .../ios/framework/Source/FlutterEngine.mm | 5 ++- .../ios/framework/Source/FlutterEngineTest.mm | 2 +- .../shell/platform/embedder/embedder.cc | 14 +++++-- .../platform/fuchsia/flutter/component_v2.cc | 5 ++- .../src/flutter/shell/testing/tester_main.cc | 10 +++-- engine/src/flutter/testing/dart_fixture.cc | 6 ++- 36 files changed, 292 insertions(+), 91 deletions(-) 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(); };