Allow embedders to specify their own task runner interfaces. (flutter/engine#8273)

Currently, all Flutter threads are managed by the engine itself. This works for
all threads except the platform thread. On this thread, the engine cannot see
the underlying event multiplexing mechanism. Using the new task runner
interfaces, the engine can relinquish the task of setting up the event
multiplexing mechanism and instead have the embedder provide one for it during
setup.

This scheme is only wired up for the platform thread. But, the eventual goal
is to expose this message loop interoperability for all threads.
This commit is contained in:
Chinmay Garde 2019-03-27 16:16:59 -07:00 committed by GitHub
parent 2f36f159f1
commit 20fe654ef3
16 changed files with 705 additions and 40 deletions

View File

@ -675,12 +675,17 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_engine.h
FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.h
FILE: ../../../flutter/shell/platform/embedder/embedder_include.c
FILE: ../../../flutter/shell/platform/embedder/embedder_safe_access.h
FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.h
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h
FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.h
FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.h
FILE: ../../../flutter/shell/platform/embedder/fixtures/a11y_main.dart
FILE: ../../../flutter/shell/platform/embedder/fixtures/simple_main.dart
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc

View File

@ -17,6 +17,8 @@ class MessageLoopImpl;
class TaskRunner : public fml::RefCountedThreadSafe<TaskRunner> {
public:
virtual ~TaskRunner();
virtual void PostTask(fml::closure task);
virtual void PostTaskForTime(fml::closure task, fml::TimePoint target_time);
@ -25,8 +27,6 @@ class TaskRunner : public fml::RefCountedThreadSafe<TaskRunner> {
virtual bool RunsTasksOnCurrentThread();
virtual ~TaskRunner();
static void RunNowOrPostTask(fml::RefPtr<fml::TaskRunner> runner,
fml::closure task);

View File

@ -22,12 +22,17 @@ source_set("embedder") {
"embedder_external_texture_gl.cc",
"embedder_external_texture_gl.h",
"embedder_include.c",
"embedder_safe_access.h",
"embedder_surface.cc",
"embedder_surface.h",
"embedder_surface_gl.cc",
"embedder_surface_gl.h",
"embedder_surface_software.cc",
"embedder_surface_software.h",
"embedder_task_runner.cc",
"embedder_task_runner.h",
"embedder_thread_host.cc",
"embedder_thread_host.h",
"platform_view_embedder.cc",
"platform_view_embedder.h",
"vsync_waiter_embedder.cc",

View File

@ -21,10 +21,6 @@ extern const intptr_t kPlatformStrongDillSize;
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
}
#include "flutter/shell/platform/embedder/embedder.h"
#include <type_traits>
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/command_line.h"
@ -38,18 +34,11 @@ extern const intptr_t kPlatformStrongDillSize;
#include "flutter/shell/common/switches.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/embedder_engine.h"
#include "flutter/shell/platform/embedder/embedder_safe_access.h"
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
#include "flutter/shell/platform/embedder/platform_view_embedder.h"
#define SAFE_ACCESS(pointer, member, default_value) \
([=]() { \
if (offsetof(std::remove_pointer<decltype(pointer)>::type, member) + \
sizeof(pointer->member) <= \
pointer->struct_size) { \
return pointer->member; \
} \
return static_cast<decltype(pointer->member)>((default_value)); \
})()
static FlutterEngineResult LogEmbedderError(FlutterEngineResult code,
const char* name,
const char* function,
@ -408,20 +397,6 @@ FlutterEngineResult FlutterEngineRun(size_t version,
};
}
// Create a thread host with the current thread as the platform thread and all
// other threads managed.
shell::ThreadHost thread_host("io.flutter", shell::ThreadHost::Type::GPU |
shell::ThreadHost::Type::IO |
shell::ThreadHost::Type::UI);
fml::MessageLoop::EnsureInitializedForCurrentThread();
blink::TaskRunners task_runners(
"io.flutter",
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
thread_host.gpu_thread->GetTaskRunner(), // gpu
thread_host.ui_thread->GetTaskRunner(), // ui
thread_host.io_thread->GetTaskRunner() // io
);
shell::PlatformViewEmbedder::UpdateSemanticsNodesCallback
update_semantics_nodes_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) {
@ -602,6 +577,23 @@ FlutterEngineResult FlutterEngineRun(size_t version,
}
}
auto thread_host =
shell::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
SAFE_ACCESS(args, custom_task_runners, nullptr));
if (!thread_host || !thread_host->IsValid()) {
FML_LOG(ERROR) << "Could not setup or infer thread configuration to run "
"the Flutter engine on.";
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto task_runners = thread_host->GetTaskRunners();
if (!task_runners.IsValid()) {
FML_LOG(ERROR) << "Task runner configuration specified is invalid.";
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
// Step 1: Create the engine.
auto embedder_engine =
std::make_unique<shell::EmbedderEngine>(std::move(thread_host), //
@ -940,3 +932,18 @@ FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine,
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
uint64_t FlutterEngineGetCurrentTime() {
return fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
}
FlutterEngineResult FlutterEngineRunTask(FlutterEngine engine,
const FlutterTask* task) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
return reinterpret_cast<shell::EmbedderEngine*>(engine)->RunTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments);
}

View File

@ -447,6 +447,52 @@ typedef void (*FlutterUpdateSemanticsCustomActionCallback)(
const FlutterSemanticsCustomAction* /* semantics custom action */,
void* /* user data */);
typedef struct _FlutterTaskRunner* FlutterTaskRunner;
typedef struct {
FlutterTaskRunner runner;
uint64_t task;
} FlutterTask;
typedef void (*FlutterTaskRunnerPostTaskCallback)(
FlutterTask /* task */,
uint64_t /* target time nanos */,
void* /* user data */);
// An interface used by the Flutter engine to execute tasks at the target time
// on a specified thread. There should be a 1-1 relationship between a thread
// and a task runner. It is undefined behavior to run a task on a thread that is
// not associated with its task runner.
typedef struct {
// The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
size_t struct_size;
void* user_data;
// May be called from any thread. Should return true if tasks posted on the
// calling thread will be run on that same thread.
//
// This field is required.
BoolCallback runs_task_on_current_thread_callback;
// May be called from any thread. The given task should be executed by the
// embedder on the thread associated with that task runner by calling
// |FlutterEngineRunTask| at the given target time. The system monotonic clock
// should be used for the target time. The target time is the absolute time
// from epoch (NOT a delta) at which the task must be returned back to the
// engine on the correct thread. If the embedder needs to calculate a delta,
// |FlutterEngineGetCurrentTime| may be called and the difference used as the
// delta.
//
// This field is required.
FlutterTaskRunnerPostTaskCallback post_task_callback;
} FlutterTaskRunnerDescription;
typedef struct {
// The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
size_t struct_size;
// Specify the task runner for the thread on which the |FlutterEngineRun| call
// is made.
const FlutterTaskRunnerDescription* platform_task_runner;
} FlutterCustomTaskRunners;
typedef struct {
// The size of this struct. Must be sizeof(FlutterProjectArgs).
size_t struct_size;
@ -572,6 +618,11 @@ typedef struct {
// away. Usually, this is done using the `@pragma('vm:entry-point')`
// decoration.
const char* custom_dart_entrypoint;
// Typically the Flutter engine create and manages its internal threads. This
// optional argument allows for the specification of task runner interfaces to
// event loops managed by the embedder on threads it creates.
const FlutterCustomTaskRunners* custom_task_runners;
} FlutterProjectArgs;
FLUTTER_EXPORT
@ -715,6 +766,19 @@ FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine,
VoidCallback callback,
void* callback_data);
// Get the current time in nanoseconds from the clock used by the flutter
// engine. This is the system monotonic clock.
FLUTTER_EXPORT
uint64_t FlutterEngineGetCurrentTime();
// Inform the engine to run the specified task. This task has been given to
// the engine via the |FlutterTaskRunnerDescription.post_task_callback|. This
// call must only be made at the target time specified in that callback. Running
// the task before that time is undefined behavior.
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineRunTask(FlutterEngine engine,
const FlutterTask* task);
#if defined(__cplusplus)
} // extern "C"
#endif

View File

@ -10,7 +10,7 @@
namespace shell {
EmbedderEngine::EmbedderEngine(
ThreadHost thread_host,
std::unique_ptr<EmbedderThreadHost> thread_host,
blink::TaskRunners task_runners,
blink::Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
@ -23,7 +23,11 @@ EmbedderEngine::EmbedderEngine(
on_create_platform_view,
on_create_rasterizer)),
external_texture_callback_(external_texture_callback) {
is_valid_ = shell_ != nullptr;
if (!shell_) {
return;
}
is_valid_ = true;
}
EmbedderEngine::~EmbedderEngine() = default;
@ -212,4 +216,12 @@ bool EmbedderEngine::PostRenderThreadTask(fml::closure task) {
return true;
}
bool EmbedderEngine::RunTask(const FlutterTask* task) {
if (!IsValid() || task == nullptr) {
return false;
}
return thread_host_->PostTask(reinterpret_cast<int64_t>(task->runner),
task->task);
}
} // namespace shell

View File

@ -6,6 +6,7 @@
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_
#include <memory>
#include <unordered_map>
#include "flutter/fml/macros.h"
#include "flutter/shell/common/shell.h"
@ -13,6 +14,7 @@
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/embedder_engine.h"
#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h"
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
namespace shell {
@ -20,7 +22,7 @@ namespace shell {
// instance of the Flutter engine.
class EmbedderEngine {
public:
EmbedderEngine(ThreadHost thread_host,
EmbedderEngine(std::unique_ptr<EmbedderThreadHost> thread_host,
blink::TaskRunners task_runners,
blink::Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
@ -65,8 +67,10 @@ class EmbedderEngine {
bool PostRenderThreadTask(fml::closure task);
bool RunTask(const FlutterTask* task);
private:
const ThreadHost thread_host_;
const std::unique_ptr<EmbedderThreadHost> thread_host_;
std::unique_ptr<Shell> shell_;
const EmbedderExternalTextureGL::ExternalTextureCallback
external_texture_callback_;

View File

@ -0,0 +1,20 @@
// 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.
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_
#include <type_traits>
#define SAFE_ACCESS(pointer, member, default_value) \
([=]() { \
if (offsetof(std::remove_pointer<decltype(pointer)>::type, member) + \
sizeof(pointer->member) <= \
pointer->struct_size) { \
return pointer->member; \
} \
return static_cast<decltype(pointer->member)>((default_value)); \
})()
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_

View File

@ -0,0 +1,72 @@
// 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.
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
#include "flutter/fml/message_loop_impl.h"
namespace shell {
EmbedderTaskRunner::EmbedderTaskRunner(DispatchTable table)
: TaskRunner(nullptr /* loop implemenation*/),
dispatch_table_(std::move(table)) {
FML_DCHECK(dispatch_table_.post_task_callback);
FML_DCHECK(dispatch_table_.runs_task_on_current_thread_callback);
}
EmbedderTaskRunner::~EmbedderTaskRunner() = default;
void EmbedderTaskRunner::PostTask(fml::closure task) {
PostTaskForTime(task, fml::TimePoint::Now());
}
void EmbedderTaskRunner::PostTaskForTime(fml::closure task,
fml::TimePoint target_time) {
if (!task) {
return;
}
uint64_t baton = 0;
{
// Release the lock before the jump via the dispatch table.
std::lock_guard<std::mutex> lock(tasks_mutex_);
baton = ++last_baton_;
pending_tasks_[baton] = task;
}
dispatch_table_.post_task_callback(this, baton, target_time);
}
void EmbedderTaskRunner::PostDelayedTask(fml::closure task,
fml::TimeDelta delay) {
PostTaskForTime(task, fml::TimePoint::Now() + delay);
}
bool EmbedderTaskRunner::RunsTasksOnCurrentThread() {
return dispatch_table_.runs_task_on_current_thread_callback();
}
bool EmbedderTaskRunner::PostTask(uint64_t baton) {
fml::closure task;
{
std::lock_guard<std::mutex> lock(tasks_mutex_);
auto found = pending_tasks_.find(baton);
if (found == pending_tasks_.end()) {
FML_LOG(ERROR) << "Embedder attempted to post an unknown task.";
return false;
}
task = found->second;
pending_tasks_.erase(found);
// Let go of the tasks mutex befor executing the task.
}
FML_DCHECK(task);
task();
return true;
}
} // namespace shell

View File

@ -0,0 +1,57 @@
// 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.
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_TASK_RUNNER_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_TASK_RUNNER_H_
#include <mutex>
#include <unordered_map>
#include "flutter/fml/macros.h"
#include "flutter/fml/synchronization/thread_annotations.h"
#include "flutter/fml/task_runner.h"
namespace shell {
class EmbedderTaskRunner final : public fml::TaskRunner {
public:
struct DispatchTable {
std::function<void(EmbedderTaskRunner* task_runner,
uint64_t task_baton,
fml::TimePoint target_time)>
post_task_callback;
std::function<bool(void)> runs_task_on_current_thread_callback;
};
EmbedderTaskRunner(DispatchTable table);
~EmbedderTaskRunner() override;
bool PostTask(uint64_t baton);
// |fml::TaskRunner|
void PostTask(fml::closure task) override;
// |fml::TaskRunner|
void PostTaskForTime(fml::closure task, fml::TimePoint target_time) override;
// |fml::TaskRunner|
void PostDelayedTask(fml::closure task, fml::TimeDelta delay) override;
// |fml::TaskRunner|
bool RunsTasksOnCurrentThread() override;
private:
DispatchTable dispatch_table_;
std::mutex tasks_mutex_;
uint64_t last_baton_ FML_GUARDED_BY(tasks_mutex_);
std::unordered_map<uint64_t, fml::closure> pending_tasks_
FML_GUARDED_BY(tasks_mutex_);
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTaskRunner);
};
} // namespace shell
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_TASK_RUNNER_H_

View File

@ -0,0 +1,204 @@
// 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.
// This is why we can't yet export the UI thread to embedders.
#define FML_USED_ON_EMBEDDER
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
#include "flutter/fml/message_loop.h"
#include "flutter/shell/platform/embedder/embedder_safe_access.h"
namespace shell {
static fml::RefPtr<EmbedderTaskRunner> CreateEmbedderTaskRunner(
const FlutterTaskRunnerDescription* description) {
if (description == nullptr) {
return {};
}
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 {};
}
if (SAFE_ACCESS(description, post_task_callback, nullptr) == nullptr) {
FML_LOG(ERROR)
<< "FlutterTaskRunnerDescription.post_task_callback was nullptr.";
return {};
}
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 fml::MakeRefCounted<EmbedderTaskRunner>(task_runner_dispatch_table);
}
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. We don't want to 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;
}
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;
}
const auto platform_task_runner = CreateEmbedderTaskRunner(
SAFE_ACCESS(custom_task_runners, platform_task_runner, nullptr));
// TODO(chinmaygarde): Add more here as we allow more threads to be controlled
// by the embedder. Create fallbacks as necessary.
if (!platform_task_runner) {
return nullptr;
}
ThreadHost thread_host(kFlutterThreadName, ThreadHost::Type::GPU |
ThreadHost::Type::IO |
ThreadHost::Type::UI);
blink::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>> embedder_task_runners;
embedder_task_runners.insert(platform_task_runner);
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);
fml::MessageLoop::EnsureInitializedForCurrentThread();
// 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 = fml::MessageLoop::GetCurrent().GetTaskRunner();
blink::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,
blink::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 blink::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 shell

View File

@ -0,0 +1,54 @@
// 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.
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_THREAD_HOST_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_THREAD_HOST_H_
#include <map>
#include <memory>
#include <set>
#include "flutter/common/task_runners.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
namespace shell {
class EmbedderThreadHost {
public:
static std::unique_ptr<EmbedderThreadHost>
CreateEmbedderOrEngineManagedThreadHost(
const FlutterCustomTaskRunners* custom_task_runners);
EmbedderThreadHost(
ThreadHost host,
blink::TaskRunners runners,
std::set<fml::RefPtr<EmbedderTaskRunner>> embedder_task_runners);
~EmbedderThreadHost();
bool IsValid() const;
const blink::TaskRunners& GetTaskRunners() const;
bool PostTask(int64_t runner, uint64_t task) const;
private:
ThreadHost host_;
blink::TaskRunners runners_;
std::map<int64_t, fml::RefPtr<EmbedderTaskRunner>> runners_map_;
static std::unique_ptr<EmbedderThreadHost> CreateEmbedderManagedThreadHost(
const FlutterCustomTaskRunners* custom_task_runners);
static std::unique_ptr<EmbedderThreadHost> CreateEngineManagedThreadHost();
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderThreadHost);
};
} // namespace shell
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_THREAD_HOST_H_

View File

@ -1,3 +1,5 @@
import 'dart:ui';
void main() {}
@pragma('vm:entry-point')
@ -18,3 +20,9 @@ void customEntrypoint1() {
void sayHiFromCustomEntrypoint1() native "SayHiFromCustomEntrypoint1";
void sayHiFromCustomEntrypoint2() native "SayHiFromCustomEntrypoint2";
void sayHiFromCustomEntrypoint3() native "SayHiFromCustomEntrypoint3";
@pragma('vm:entry-point')
void invokePlatformTaskRunner() {
window.sendPlatformMessage('OhHi', null, null);
}

View File

@ -13,6 +13,7 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
: context_(context) {
project_args_.struct_size = sizeof(project_args_);
software_renderer_config_.struct_size = sizeof(FlutterSoftwareRendererConfig);
custom_task_runners_.struct_size = sizeof(FlutterCustomTaskRunners);
software_renderer_config_.surface_present_callback =
[](void*, const void*, size_t, size_t) { return true; };
@ -71,8 +72,43 @@ void EmbedderConfigBuilder::SetDartEntrypoint(std::string entrypoint) {
project_args_.custom_dart_entrypoint = dart_entrypoint_.c_str();
}
UniqueEngine EmbedderConfigBuilder::LaunchEngine() const {
void EmbedderConfigBuilder::AddCommandLineArgument(std::string arg) {
if (arg.size() == 0) {
return;
}
command_line_arguments_.emplace_back(std::move(arg));
}
void EmbedderConfigBuilder::SetPlatformTaskRunner(
const FlutterTaskRunnerDescription* runner) {
if (runner == nullptr) {
return;
}
custom_task_runners_.platform_task_runner = runner;
project_args_.custom_task_runners = &custom_task_runners_;
}
UniqueEngine EmbedderConfigBuilder::LaunchEngine() {
FlutterEngine engine = nullptr;
std::vector<const char*> args;
args.reserve(command_line_arguments_.size());
for (const auto& arg : command_line_arguments_) {
args.push_back(arg.c_str());
}
if (args.size() > 0) {
project_args_.command_line_argv = args.data();
project_args_.command_line_argc = args.size();
} else {
// Clear it out in case this is not the first engine launch from the
// embedder config builder.
project_args_.command_line_argv = nullptr;
project_args_.command_line_argc = 0;
}
auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_,
&project_args_, &context_, &engine);

View File

@ -19,7 +19,7 @@ struct UniqueEngineTraits {
static bool IsValid(const FlutterEngine& value) { return value != nullptr; }
static void Free(FlutterEngine engine) {
static void Free(FlutterEngine& engine) {
auto result = FlutterEngineShutdown(engine);
FML_CHECK(result == kSuccess);
}
@ -50,7 +50,11 @@ class EmbedderConfigBuilder {
void SetDartEntrypoint(std::string entrypoint);
UniqueEngine LaunchEngine() const;
void AddCommandLineArgument(std::string arg);
void SetPlatformTaskRunner(const FlutterTaskRunnerDescription* runner);
UniqueEngine LaunchEngine();
private:
EmbedderContext& context_;
@ -58,6 +62,8 @@ class EmbedderConfigBuilder {
FlutterRendererConfig renderer_config_ = {};
FlutterSoftwareRendererConfig software_renderer_config_ = {};
std::string dart_entrypoint_;
FlutterCustomTaskRunners custom_task_runners_ = {};
std::vector<std::string> command_line_arguments_;
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderConfigBuilder);
};

View File

@ -2,11 +2,17 @@
// 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 <string>
#include "embedder.h"
#include "flutter/fml/file.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/thread.h"
#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h"
#include "flutter/shell/platform/embedder/tests/embedder_test.h"
#include "flutter/testing/testing.h"
@ -68,7 +74,7 @@ TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
// Can be defined separately.
auto entry1 = [&latch1](Dart_NativeArguments args) {
FML_LOG(ERROR) << "In Callback 1";
FML_LOG(INFO) << "In Callback 1";
latch1.Signal();
};
auto native_entry1 = CREATE_NATIVE_ENTRY(entry1);
@ -76,7 +82,7 @@ TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
// Can be wrapped in in the args.
auto entry2 = [&latch2](Dart_NativeArguments args) {
FML_LOG(ERROR) << "In Callback 2";
FML_LOG(INFO) << "In Callback 2";
latch2.Signal();
};
context.AddNativeCallback("SayHiFromCustomEntrypoint2",
@ -86,7 +92,7 @@ TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
context.AddNativeCallback(
"SayHiFromCustomEntrypoint3",
CREATE_NATIVE_ENTRY([&latch3](Dart_NativeArguments args) {
FML_LOG(ERROR) << "In Callback 3";
FML_LOG(INFO) << "In Callback 3";
latch3.Signal();
}));
@ -99,5 +105,110 @@ TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
ASSERT_TRUE(engine.is_valid());
}
class EmbedderTestTaskRunner {
public:
EmbedderTestTaskRunner(std::function<void(FlutterTask)> on_forward_task)
: on_forward_task_(on_forward_task) {}
void SetForwardingTaskRunner(fml::RefPtr<fml::TaskRunner> runner) {
forwarding_target_ = std::move(runner);
}
FlutterTaskRunnerDescription GetEmbedderDescription() {
FlutterTaskRunnerDescription desc;
desc.struct_size = sizeof(desc);
desc.user_data = this;
desc.runs_task_on_current_thread_callback = [](void* user_data) -> bool {
return reinterpret_cast<EmbedderTestTaskRunner*>(user_data)
->forwarding_target_->RunsTasksOnCurrentThread();
};
desc.post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
void* user_data) -> void {
auto runner = reinterpret_cast<EmbedderTestTaskRunner*>(user_data);
auto target_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(target_time_nanos));
runner->forwarding_target_->PostTaskForTime(
[task, forwarder = runner->on_forward_task_]() { forwarder(task); },
target_time);
};
return desc;
}
private:
fml::RefPtr<fml::TaskRunner> forwarding_target_;
std::function<void(FlutterTask)> on_forward_task_;
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestTaskRunner);
};
TEST_F(EmbedderTest, CanSpecifyCustomTaskRunner) {
auto& context = GetEmbedderContext();
fml::AutoResetWaitableEvent latch;
// Run the test on its own thread with a message loop so that it san safely
// pump its event loop while we wait for all the conditions to be checked.
fml::Thread thread;
UniqueEngine engine;
bool signalled = false;
EmbedderTestTaskRunner runner([&](FlutterTask task) {
// There may be multiple tasks posted but we only need to check assertions
// once.
if (signalled) {
// Since we have the baton, return it back to the engine. We don't care
// about the return value because the engine could be shutting down an it
// may not actually be able to accept the same.
FlutterEngineRunTask(engine.get(), &task);
return;
}
signalled = true;
FML_LOG(INFO) << "Checking assertions.";
ASSERT_TRUE(engine.is_valid());
ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
latch.Signal();
});
thread.GetTaskRunner()->PostTask([&]() {
EmbedderConfigBuilder builder(context);
builder.AddCommandLineArgument("--verbose-logging");
const auto task_runner_description = runner.GetEmbedderDescription();
runner.SetForwardingTaskRunner(
fml::MessageLoop::GetCurrent().GetTaskRunner());
builder.SetPlatformTaskRunner(&task_runner_description);
builder.SetDartEntrypoint("invokePlatformTaskRunner");
engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
});
// Signalled when all the assertions are checked.
latch.Wait();
FML_LOG(INFO) << "Assertions checked. Killing engine.";
ASSERT_TRUE(engine.is_valid());
// Since the engine was started on its own thread, it must be killed there as
// well.
fml::AutoResetWaitableEvent kill_latch;
thread.GetTaskRunner()->PostTask(
fml::MakeCopyable([&engine, &kill_latch]() mutable {
engine.reset();
FML_LOG(INFO) << "Engine killed.";
kill_latch.Signal();
}));
kill_latch.Wait();
ASSERT_TRUE(signalled);
}
TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) {
auto point1 = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(FlutterEngineGetCurrentTime()));
auto point2 = fml::TimePoint::Now();
ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1));
}
} // namespace testing
} // namespace shell