blob: 73646cfc0cb0951b785bfdaf414ef648f9985e03 [file] [log] [blame]
// 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 <algorithm>
#include "flutter/fml/message_loop.h"
#include "flutter/shell/platform/embedder/embedder_struct_macros.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<bool, fml::RefPtr<EmbedderTaskRunner>>
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<FlutterTaskRunner>(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<EmbedderTaskRunner>(
task_runner_dispatch_table,
SAFE_ACCESS(description, identifier, 0u))};
}
std::unique_ptr<EmbedderThreadHost>
EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
const FlutterCustomTaskRunners* custom_task_runners,
const flutter::ThreadConfigSetter& config_setter) {
{
auto host =
CreateEmbedderManagedThreadHost(custom_task_runners, config_setter);
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(config_setter);
if (host && host->IsValid()) {
return host;
}
}
return nullptr;
}
static fml::RefPtr<fml::TaskRunner> GetCurrentThreadTaskRunner() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
return fml::MessageLoop::GetCurrent().GetTaskRunner();
}
constexpr const char* kFlutterThreadName = "io.flutter";
fml::Thread::ThreadConfig MakeThreadConfig(
flutter::ThreadHost::Type type,
fml::Thread::ThreadPriority priority) {
return fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(type,
kFlutterThreadName),
priority);
}
// static
std::unique_ptr<EmbedderThreadHost>
EmbedderThreadHost::CreateEmbedderManagedThreadHost(
const FlutterCustomTaskRunners* custom_task_runners,
const flutter::ThreadConfigSetter& config_setter) {
if (custom_task_runners == nullptr) {
return nullptr;
}
auto thread_host_config = ThreadHost::ThreadHostConfig(config_setter);
// 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.
thread_host_config.SetUIConfig(MakeThreadConfig(
ThreadHost::Type::UI, fml::Thread::ThreadPriority::DISPLAY));
thread_host_config.SetIOConfig(MakeThreadConfig(
ThreadHost::Type::IO, fml::Thread::ThreadPriority::BACKGROUND));
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 raster task runner, one needs to be
// created.
if (!render_task_runner_pair.second) {
thread_host_config.SetRasterConfig(MakeThreadConfig(
ThreadHost::Type::RASTER, fml::Thread::ThreadPriority::RASTER));
}
// If both the platform task runner and the raster 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(thread_host_config);
// 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<fml::RefPtr<fml::TaskRunner>>(
platform_task_runner_pair.second)
: GetCurrentThreadTaskRunner();
// If the embedder has supplied a raster task runner, use that. If not, use
// the one from our thread host.
auto render_task_runner = render_task_runner_pair.second
? static_cast<fml::RefPtr<fml::TaskRunner>>(
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<fml::RefPtr<EmbedderTaskRunner>> 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<EmbedderThreadHost>(
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>
EmbedderThreadHost::CreateEngineManagedThreadHost(
const flutter::ThreadConfigSetter& config_setter) {
// Crate a thraed host config, and specified the thread name and priority.
auto thread_host_config = ThreadHost::ThreadHostConfig(config_setter);
thread_host_config.SetUIConfig(MakeThreadConfig(
flutter::ThreadHost::UI, fml::Thread::ThreadPriority::DISPLAY));
thread_host_config.SetRasterConfig(MakeThreadConfig(
flutter::ThreadHost::RASTER, fml::Thread::ThreadPriority::RASTER));
thread_host_config.SetIOConfig(MakeThreadConfig(
flutter::ThreadHost::IO, fml::Thread::ThreadPriority::BACKGROUND));
// Create a thread host with the current thread as the platform thread and all
// other threads managed.
ThreadHost thread_host(thread_host_config);
// 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<fml::RefPtr<EmbedderTaskRunner>> empty_embedder_task_runners;
auto embedder_host = std::make_unique<EmbedderThreadHost>(
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,
const flutter::TaskRunners& runners,
const std::set<fml::RefPtr<EmbedderTaskRunner>>& embedder_task_runners)
: host_(std::move(host)), runners_(runners) {
for (const auto& runner : embedder_task_runners) {
runners_map_[reinterpret_cast<int64_t>(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