mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
We'd like to (or already are) using the concurrent message loop for high priority rendering tasks like PSO construction and render pass encoding. The default priority level for the engine managed concurrent message loop is 2, which is a significantly lower priority than the raster thread at -5. This is almost certainly causing priority inversion. We must move back to dedicated runners so we can adjust thread priorities.
183 lines
4.7 KiB
C++
183 lines
4.7 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.
|
|
|
|
#include "flutter/fml/concurrent_message_loop.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "flutter/fml/thread.h"
|
|
#include "flutter/fml/trace_event.h"
|
|
|
|
namespace fml {
|
|
|
|
ConcurrentMessageLoop::ConcurrentMessageLoop(size_t worker_count)
|
|
: worker_count_(std::max<size_t>(worker_count, 1ul)) {
|
|
for (size_t i = 0; i < worker_count_; ++i) {
|
|
workers_.emplace_back([i, this]() {
|
|
fml::Thread::SetCurrentThreadName(fml::Thread::ThreadConfig(
|
|
std::string{"io.worker." + std::to_string(i + 1)}));
|
|
WorkerMain();
|
|
});
|
|
}
|
|
|
|
for (const auto& worker : workers_) {
|
|
worker_thread_ids_.emplace_back(worker.get_id());
|
|
}
|
|
}
|
|
|
|
ConcurrentMessageLoop::~ConcurrentMessageLoop() {
|
|
Terminate();
|
|
for (auto& worker : workers_) {
|
|
FML_DCHECK(worker.joinable());
|
|
worker.join();
|
|
}
|
|
}
|
|
|
|
size_t ConcurrentMessageLoop::GetWorkerCount() const {
|
|
return worker_count_;
|
|
}
|
|
|
|
std::shared_ptr<ConcurrentTaskRunner> ConcurrentMessageLoop::GetTaskRunner() {
|
|
return std::make_shared<ConcurrentTaskRunner>(weak_from_this());
|
|
}
|
|
|
|
void ConcurrentMessageLoop::PostTask(const fml::closure& task) {
|
|
if (!task) {
|
|
return;
|
|
}
|
|
|
|
std::unique_lock lock(tasks_mutex_);
|
|
|
|
// Don't just drop tasks on the floor in case of shutdown.
|
|
if (shutdown_) {
|
|
FML_DLOG(WARNING)
|
|
<< "Tried to post a task to shutdown concurrent message "
|
|
"loop. The task will be executed on the callers thread.";
|
|
lock.unlock();
|
|
ExecuteTask(task);
|
|
return;
|
|
}
|
|
|
|
tasks_.push(task);
|
|
|
|
// Unlock the mutex before notifying the condition variable because that mutex
|
|
// has to be acquired on the other thread anyway. Waiting in this scope till
|
|
// it is acquired there is a pessimization.
|
|
lock.unlock();
|
|
|
|
tasks_condition_.notify_one();
|
|
}
|
|
|
|
void ConcurrentMessageLoop::WorkerMain() {
|
|
while (true) {
|
|
std::unique_lock lock(tasks_mutex_);
|
|
tasks_condition_.wait(lock, [&]() {
|
|
return !tasks_.empty() || shutdown_ || HasThreadTasksLocked();
|
|
});
|
|
|
|
// Shutdown cannot be read with the task mutex unlocked.
|
|
bool shutdown_now = shutdown_;
|
|
fml::closure task;
|
|
std::vector<fml::closure> thread_tasks;
|
|
|
|
if (!tasks_.empty()) {
|
|
task = tasks_.front();
|
|
tasks_.pop();
|
|
}
|
|
|
|
if (HasThreadTasksLocked()) {
|
|
thread_tasks = GetThreadTasksLocked();
|
|
FML_DCHECK(!HasThreadTasksLocked());
|
|
}
|
|
|
|
// Don't hold onto the mutex while tasks are being executed as they could
|
|
// themselves try to post more tasks to the message loop.
|
|
lock.unlock();
|
|
|
|
TRACE_EVENT0("flutter", "ConcurrentWorkerWake");
|
|
// Execute the primary task we woke up for.
|
|
if (task) {
|
|
ExecuteTask(task);
|
|
}
|
|
|
|
// Execute any thread tasks.
|
|
for (const auto& thread_task : thread_tasks) {
|
|
ExecuteTask(thread_task);
|
|
}
|
|
|
|
if (shutdown_now) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConcurrentMessageLoop::ExecuteTask(const fml::closure& task) {
|
|
task();
|
|
}
|
|
|
|
void ConcurrentMessageLoop::Terminate() {
|
|
std::scoped_lock lock(tasks_mutex_);
|
|
shutdown_ = true;
|
|
tasks_condition_.notify_all();
|
|
}
|
|
|
|
void ConcurrentMessageLoop::PostTaskToAllWorkers(const fml::closure& task) {
|
|
if (!task) {
|
|
return;
|
|
}
|
|
|
|
std::scoped_lock lock(tasks_mutex_);
|
|
for (const auto& worker_thread_id : worker_thread_ids_) {
|
|
thread_tasks_[worker_thread_id].emplace_back(task);
|
|
}
|
|
tasks_condition_.notify_all();
|
|
}
|
|
|
|
bool ConcurrentMessageLoop::HasThreadTasksLocked() const {
|
|
return thread_tasks_.count(std::this_thread::get_id()) > 0;
|
|
}
|
|
|
|
std::vector<fml::closure> ConcurrentMessageLoop::GetThreadTasksLocked() {
|
|
auto found = thread_tasks_.find(std::this_thread::get_id());
|
|
FML_DCHECK(found != thread_tasks_.end());
|
|
std::vector<fml::closure> pending_tasks;
|
|
std::swap(pending_tasks, found->second);
|
|
thread_tasks_.erase(found);
|
|
return pending_tasks;
|
|
}
|
|
|
|
ConcurrentTaskRunner::ConcurrentTaskRunner(
|
|
std::weak_ptr<ConcurrentMessageLoop> weak_loop)
|
|
: weak_loop_(std::move(weak_loop)) {}
|
|
|
|
ConcurrentTaskRunner::~ConcurrentTaskRunner() = default;
|
|
|
|
void ConcurrentTaskRunner::PostTask(const fml::closure& task) {
|
|
if (!task) {
|
|
return;
|
|
}
|
|
|
|
if (auto loop = weak_loop_.lock()) {
|
|
loop->PostTask(task);
|
|
return;
|
|
}
|
|
|
|
FML_DLOG(WARNING)
|
|
<< "Tried to post to a concurrent message loop that has already died. "
|
|
"Executing the task on the callers thread.";
|
|
task();
|
|
}
|
|
|
|
bool ConcurrentMessageLoop::RunsTasksOnCurrentThread() {
|
|
std::scoped_lock lock(tasks_mutex_);
|
|
for (const auto& worker_thread_id : worker_thread_ids_) {
|
|
if (worker_thread_id == std::this_thread::get_id()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace fml
|