mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
bf81971f7a
Embedders may use this to specify a thread whose event loop is managed by them
instead of the engine. In addition, specifying the same task runner for both
the platform and render task runners allows embedders to effectively perform
GPU rendering operations on the platform thread.
To affect this change, the following non breaking changes to the API have been
made:
* The `FlutterCustomTaskRunners` struct now has a new field `render_task_runner`
for the specification of a custom render task runner.
* The `FlutterTaskRunnerDescription` has a new field `identifier`. Embedders
must supply a unique identifier for each task runner they specify. In
addition, when describing multiple task runners that run their tasks on the
same thread, their identifiers must match.
* The embedder may need to process tasks during `FlutterEngineRun` and
`FlutterEngineShutdown`. However, the embedder doesn't have the Flutter engine
handle before `FlutterEngineRun` and is supposed to relinquish handle right
before `FlutterEngineShutdown`. Since the embedder needs the Flutter engine
handle to service tasks on other threads while these calls are underway,
there exist opportunities for deadlock. To work around this scenario, three
new calls have been added that allow more deliberate management of the Flutter
engine instance.
* `FlutterEngineRun` can be replaced with `FlutterEngineInitialize` and
`FlutterEngineRunInitialized`. The embedder can obtain a handle to the
engine after the first call but the engine will not post any tasks to custom
task runners specified by the embedder till the
`FlutterEngineRunInitialized` call. Embedders can guard the Flutter engine
handle behind a mutex for safe task runner interop.
* `FlutterEngineShutdown` can be preceded by the `FlutterEngineDeinitialize`
call. After this call the Flutter engine will no longer post tasks onto
embedder managed task runners. It is still embedder responsibility to
collect the Flutter engine handle via `FlutterEngineShutdown`.
* To maintain backwards compatibility with the old APIs, `FlutterEngineRun` is
now just a convenience for `FlutterEngineInitialize` and
`FlutterEngineRunInitilaized`. `FlutterEngineShutdown` now implicitly calls
`FlutterEngineDeinitialize` as well. This allows existing users who don't care
are custom task runner interop to keep using the old APIs.
* Adds complete test coverage for both old and new paths.
Fixes https://github.com/flutter/flutter/issues/42460
Prerequisite for https://github.com/flutter/flutter/issues/17579
274 lines
9.8 KiB
C++
274 lines
9.8 KiB
C++
// 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_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<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) {
|
|
{
|
|
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<fml::TaskRunner> GetCurrentThreadTaskRunner() {
|
|
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
|
return fml::MessageLoop::GetCurrent().GetTaskRunner();
|
|
}
|
|
|
|
constexpr const char* kFlutterThreadName = "io.flutter";
|
|
|
|
// static
|
|
std::unique_ptr<EmbedderThreadHost>
|
|
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<fml::RefPtr<fml::TaskRunner>>(
|
|
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<fml::RefPtr<fml::TaskRunner>>(
|
|
render_task_runner_pair.second)
|
|
: thread_host.gpu_thread->GetTaskRunner();
|
|
|
|
flutter::TaskRunners task_runners(
|
|
kFlutterThreadName,
|
|
platform_task_runner, // platform
|
|
render_task_runner, // gpu
|
|
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() {
|
|
// 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.gpu_thread->GetTaskRunner(), // gpu
|
|
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,
|
|
flutter::TaskRunners runners,
|
|
std::set<fml::RefPtr<EmbedderTaskRunner>> embedder_task_runners)
|
|
: host_(std::move(host)), runners_(std::move(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
|