Skip to content

Commit

Permalink
Allow embedders to specify their own task runner interfaces.
Browse files Browse the repository at this point in the history
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 sceme is only wired up for the platform thread. But, the eventual goal
is to expose this message loop interoperability for all threads.
  • Loading branch information
chinmaygarde committed Mar 27, 2019
1 parent 039e6f6 commit f484779
Show file tree
Hide file tree
Showing 15 changed files with 679 additions and 40 deletions.
4 changes: 2 additions & 2 deletions fml/task_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
59 changes: 31 additions & 28 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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), //
Expand Down Expand Up @@ -940,3 +932,14 @@ FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine,
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency);
}

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);
}
55 changes: 55 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,48 @@ 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.
//
// This field is required.
FlutterTaskRunnerPostTaskCallback post_task_callback;
} FlutterTaskRunnerDescription;

typedef struct {
// The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
size_t struct_size;
// Sepcify the task runner for the thread on which the |FluterEngineRun| call
// is made.
const FlutterTaskRunnerDescription* platform_task_runner;
} FlutterCustomTaskRunners;

typedef struct {
// The size of this struct. Must be sizeof(FlutterProjectArgs).
size_t struct_size;
Expand Down Expand Up @@ -572,6 +614,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
Expand Down Expand Up @@ -715,6 +762,14 @@ FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine,
VoidCallback callback,
void* callback_data);

// 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
Expand Down
16 changes: 14 additions & 2 deletions shell/platform/embedder/embedder_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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
8 changes: 6 additions & 2 deletions shell/platform/embedder/embedder_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_

#include <memory>
#include <unordered_map>

#include "flutter/fml/macros.h"
#include "flutter/shell/common/shell.h"
#include "flutter/shell/common/thread_host.h"
#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 {

// The object that is returned to the embedder as an opaque pointer to the
// 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,
Expand Down Expand Up @@ -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_;
Expand Down
20 changes: 20 additions & 0 deletions shell/platform/embedder/embedder_safe_access.h
Original file line number Diff line number Diff line change
@@ -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_
72 changes: 72 additions & 0 deletions shell/platform/embedder/embedder_task_runner.cc
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit f484779

Please sign in to comment.