// 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. #define FML_USED_ON_EMBEDDER #include "flutter/shell/platform/embedder/embedder_thread_host.h" #include #include "flutter/fml/message_loop.h" #include "flutter/shell/platform/embedder/embedder_safe_access.h" namespace flutter { //------------------------------------------------------------------------------ /// @brief Attempts to create a task runner from an embedder task runner /// description. The first boolean in the pair indicate whether the /// embedder specified an invalid task runner description. In this /// case, engine launch must be aborted. If the embedder did not /// specify any task runner, an engine managed task runner and /// thread must be selected instead. /// /// @param[in] description The description /// /// @return A pair that returns if the embedder has specified a task runner /// (null otherwise) and whether to terminate further engine launch. /// static std::pair> CreateEmbedderTaskRunner(const FlutterTaskRunnerDescription* description) { if (description == nullptr) { // This is not embedder error. The embedder API will just have to create a // plain old task runner (and create a thread for it) instead of using a // task runner provided to us by the embedder. return {true, {}}; } if (SAFE_ACCESS(description, runs_task_on_current_thread_callback, nullptr) == nullptr) { FML_LOG(ERROR) << "FlutterTaskRunnerDescription.runs_task_on_current_" "thread_callback was nullptr."; return {false, {}}; } if (SAFE_ACCESS(description, post_task_callback, nullptr) == nullptr) { FML_LOG(ERROR) << "FlutterTaskRunnerDescription.post_task_callback was nullptr."; return {false, {}}; } auto user_data = SAFE_ACCESS(description, user_data, nullptr); // ABI safety checks have been completed. auto post_task_callback_c = description->post_task_callback; auto runs_task_on_current_thread_callback_c = description->runs_task_on_current_thread_callback; EmbedderTaskRunner::DispatchTable task_runner_dispatch_table = { // .post_task_callback [post_task_callback_c, user_data](EmbedderTaskRunner* task_runner, uint64_t task_baton, fml::TimePoint target_time) -> void { FlutterTask task = { // runner reinterpret_cast(task_runner), // task task_baton, }; post_task_callback_c(task, target_time.ToEpochDelta().ToNanoseconds(), user_data); }, // runs_task_on_current_thread_callback [runs_task_on_current_thread_callback_c, user_data]() -> bool { return runs_task_on_current_thread_callback_c(user_data); }}; return {true, fml::MakeRefCounted( task_runner_dispatch_table, SAFE_ACCESS(description, identifier, 0u))}; } std::unique_ptr EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost( const FlutterCustomTaskRunners* custom_task_runners) { { auto host = CreateEmbedderManagedThreadHost(custom_task_runners); if (host && host->IsValid()) { return host; } } // Only attempt to create the engine managed host if the embedder did not // specify a custom configuration. Don't fallback to the engine managed // configuration if the embedder attempted to specify a configuration but // messed up with an incorrect configuration. if (custom_task_runners == nullptr) { auto host = CreateEngineManagedThreadHost(); if (host && host->IsValid()) { return host; } } return nullptr; } static fml::RefPtr GetCurrentThreadTaskRunner() { fml::MessageLoop::EnsureInitializedForCurrentThread(); return fml::MessageLoop::GetCurrent().GetTaskRunner(); } constexpr const char* kFlutterThreadName = "io.flutter"; // static std::unique_ptr EmbedderThreadHost::CreateEmbedderManagedThreadHost( const FlutterCustomTaskRunners* custom_task_runners) { if (custom_task_runners == nullptr) { return nullptr; } // The UI and IO threads are always created by the engine and the embedder has // no opportunity to specify task runners for the same. // // If/when more task runners are exposed, this mask will need to be updated. uint64_t engine_thread_host_mask = ThreadHost::Type::UI | ThreadHost::Type::IO; auto platform_task_runner_pair = CreateEmbedderTaskRunner( SAFE_ACCESS(custom_task_runners, platform_task_runner, nullptr)); auto render_task_runner_pair = CreateEmbedderTaskRunner( SAFE_ACCESS(custom_task_runners, render_task_runner, nullptr)); if (!platform_task_runner_pair.first || !render_task_runner_pair.first) { // User error while supplying a custom task runner. Return an invalid thread // host. This will abort engine initialization. Don't fallback to defaults // if the user wanted to specify a task runner but just messed up instead. return nullptr; } // If the embedder has not supplied a GPU task runner, one needs to be // created. if (!render_task_runner_pair.second) { engine_thread_host_mask |= ThreadHost::Type::GPU; } // If both the platform task runner and the GPU task runner are specified and // have the same identifier, store only one. if (platform_task_runner_pair.second && render_task_runner_pair.second) { if (platform_task_runner_pair.second->GetEmbedderIdentifier() == render_task_runner_pair.second->GetEmbedderIdentifier()) { render_task_runner_pair.second = platform_task_runner_pair.second; } } // Create a thread host with just the threads that need to be managed by the // engine. The embedder has provided the rest. ThreadHost thread_host(kFlutterThreadName, engine_thread_host_mask); // If the embedder has supplied a platform task runner, use that. If not, use // the current thread task runner. auto platform_task_runner = platform_task_runner_pair.second ? static_cast>( platform_task_runner_pair.second) : GetCurrentThreadTaskRunner(); // If the embedder has supplied a GPU task runner, use that. If not, use the // one from our thread host. auto render_task_runner = render_task_runner_pair.second ? static_cast>( render_task_runner_pair.second) : thread_host.raster_thread->GetTaskRunner(); flutter::TaskRunners task_runners( kFlutterThreadName, platform_task_runner, // platform render_task_runner, // raster thread_host.ui_thread->GetTaskRunner(), // ui (always engine managed) thread_host.io_thread->GetTaskRunner() // io (always engine managed) ); if (!task_runners.IsValid()) { return nullptr; } std::set> embedder_task_runners; if (platform_task_runner_pair.second) { embedder_task_runners.insert(platform_task_runner_pair.second); } if (render_task_runner_pair.second) { embedder_task_runners.insert(render_task_runner_pair.second); } auto embedder_host = std::make_unique( std::move(thread_host), std::move(task_runners), std::move(embedder_task_runners)); if (embedder_host->IsValid()) { return embedder_host; } return nullptr; } // static std::unique_ptr EmbedderThreadHost::CreateEngineManagedThreadHost() { // Create a thread host with the current thread as the platform thread and all // other threads managed. ThreadHost thread_host(kFlutterThreadName, ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); // For embedder platforms that don't have native message loop interop, this // will reference a task runner that points to a null message loop // implementation. auto platform_task_runner = GetCurrentThreadTaskRunner(); flutter::TaskRunners task_runners( kFlutterThreadName, platform_task_runner, // platform thread_host.raster_thread->GetTaskRunner(), // raster thread_host.ui_thread->GetTaskRunner(), // ui thread_host.io_thread->GetTaskRunner() // io ); if (!task_runners.IsValid()) { return nullptr; } std::set> empty_embedder_task_runners; auto embedder_host = std::make_unique( std::move(thread_host), std::move(task_runners), empty_embedder_task_runners); if (embedder_host->IsValid()) { return embedder_host; } return nullptr; } EmbedderThreadHost::EmbedderThreadHost( ThreadHost host, flutter::TaskRunners runners, std::set> embedder_task_runners) : host_(std::move(host)), runners_(std::move(runners)) { for (const auto& runner : embedder_task_runners) { runners_map_[reinterpret_cast(runner.get())] = runner; } } EmbedderThreadHost::~EmbedderThreadHost() = default; bool EmbedderThreadHost::IsValid() const { return runners_.IsValid(); } const flutter::TaskRunners& EmbedderThreadHost::GetTaskRunners() const { return runners_; } bool EmbedderThreadHost::PostTask(int64_t runner, uint64_t task) const { auto found = runners_map_.find(runner); if (found == runners_map_.end()) { return false; } return found->second->PostTask(task); } } // namespace flutter