Skip to content

Commit

Permalink
Define thread priority enum and set thread priority for all threads i…
Browse files Browse the repository at this point in the history
…n Engine
  • Loading branch information
JsouLiang committed Jan 5, 2022
1 parent ab1e8f5 commit 78321b4
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 44 deletions.
21 changes: 18 additions & 3 deletions fml/thread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <memory>
#include <string>
#include <utility>

#include "flutter/fml/build_config.h"
#include "flutter/fml/message_loop.h"
Expand All @@ -23,11 +24,25 @@

namespace fml {

Thread::Thread(const std::string& name) : joined_(false) {
Thread::ThreadConfig::ThreadConfig(std::string name, ThreadPriority priority)
: thread_name_(name), thread_priority_(priority) {}

void Thread::ThreadConfig::SetCurrentThreadName() {
Thread::SetCurrentThreadName(thread_name_);
}

void Thread::ThreadConfig::SetCurrentThreadPriority() {}

Thread::Thread(const std::string& name)
: Thread(ThreadConfig::DefaultConfigure(name)) {}

Thread::Thread(std::unique_ptr<Thread::ThreadConfig> configurer)
: thread_config_(std::move(configurer)), joined_(false) {
fml::AutoResetWaitableEvent latch;
fml::RefPtr<fml::TaskRunner> runner;
thread_ = std::make_unique<std::thread>([&latch, &runner, name]() -> void {
SetCurrentThreadName(name);
thread_ = std::make_unique<std::thread>([&]() -> void {
thread_config_->SetCurrentThreadName();
thread_config_->SetCurrentThreadPriority();
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = MessageLoop::GetCurrent();
runner = loop.GetTaskRunner();
Expand Down
54 changes: 51 additions & 3 deletions fml/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <atomic>
#include <memory>
#include <string>
#include <thread>

#include "flutter/fml/macros.h"
Expand All @@ -16,18 +17,65 @@ namespace fml {

class Thread {
public:
explicit Thread(const std::string& name = "");
// Valid values for priority of Thread
enum class ThreadPriority : int {
// Suitable for threads that shouldn't disrupt high priority work.
BACKGROUND,
// Default priority level.
NORMAL,
// Suitable for threads which generate data for the display.
DISPLAY,
// Suitable for thread which raster data
RASTER,
};

/// The ThreadConfig is used for setting thread perorities
class ThreadConfig {
public:
explicit ThreadConfig(std::string name = "",
ThreadPriority priority = ThreadPriority::NORMAL);

static std::unique_ptr<ThreadConfig> DefaultConfigure(
const std::string& name = "") {
return std::make_unique<ThreadConfig>(name);
}

ThreadPriority thread_priority() const { return thread_priority_; }

const std::string& thread_name() const { return thread_name_; }

/// Set current thread name
virtual void SetCurrentThreadName();

/// default do nothing, which mean user can use platform api to set priority
/// example: iOS might use pthread_qos set thread priority, Android might
/// use ::setPriority set thread priority
virtual void SetCurrentThreadPriority();

virtual ~ThreadConfig() = default;

private:
const std::string thread_name_;
ThreadPriority thread_priority_;
};

explicit Thread(const std::string& name);

explicit Thread(std::unique_ptr<ThreadConfig> configurer =
ThreadConfig::DefaultConfigure());

~Thread();

fml::RefPtr<fml::TaskRunner> GetTaskRunner() const;

void Join();

static void SetCurrentThreadName(const std::string& name);

void Join();

private:
std::unique_ptr<std::thread> thread_;
/// ThreadConfigure is used for setting thread configure some like `priority`
std::unique_ptr<ThreadConfig> thread_config_;
fml::RefPtr<fml::TaskRunner> task_runner_;
std::atomic_bool joined_;

Expand Down
80 changes: 80 additions & 0 deletions fml/thread_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

#include "flutter/fml/thread.h"

#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID)
#include <pthread.h>
#endif

#include <memory>
#include "gtest/gtest.h"

TEST(Thread, CanStartAndEnd) {
Expand All @@ -24,3 +29,78 @@ TEST(Thread, HasARunningMessageLoop) {
thread.Join();
ASSERT_TRUE(done);
}

#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID)
TEST(Thread, ThreadNameCreatedWithConfig) {
const std::string name = "Thread1";
fml::Thread thread(fml::Thread::ThreadConfig::DefaultConfigure(name));

bool done = false;
constexpr int NAMELEN = 8;
thread.GetTaskRunner()->PostTask([&done, &name]() {
done = true;
char thread_name[NAMELEN];
pthread_t current_thread = pthread_self();
pthread_getname_np(current_thread, thread_name, NAMELEN);
ASSERT_EQ(thread_name, name);
});
thread.Join();
ASSERT_TRUE(done);
}

class MockThreadConfig : public fml::Thread::ThreadConfig {
public:
using fml::Thread::ThreadConfig::ThreadConfig;

void SetCurrentThreadPriority() override {
pthread_t tid = pthread_self();
struct sched_param param;
int policy = SCHED_OTHER;
switch (thread_priority()) {
case fml::Thread::ThreadPriority::DISPLAY:
param.sched_priority = 10;
break;
default:
param.sched_priority = 1;
}
pthread_setschedparam(tid, policy, &param);
}
};

TEST(Thread, ThreadPriorityCreatedWithConfig) {
const std::string thread1_name = "Thread1";
const std::string thread2_name = "Thread2";
fml::Thread thread(std::make_unique<MockThreadConfig>(
thread1_name, fml::Thread::ThreadPriority::NORMAL));

bool done = false;
constexpr int NAMELEN = 8;
struct sched_param param;
int policy;
thread.GetTaskRunner()->PostTask([&]() {
done = true;
char thread_name[NAMELEN];
pthread_t current_thread = pthread_self();
pthread_getname_np(current_thread, thread_name, NAMELEN);
pthread_getschedparam(current_thread, &policy, &param);
ASSERT_EQ(thread_name, thread1_name);
ASSERT_EQ(policy, SCHED_OTHER);
ASSERT_EQ(param.sched_priority, 1);
});

fml::Thread thread2(std::make_unique<MockThreadConfig>(
thread2_name, fml::Thread::ThreadPriority::DISPLAY));
thread2.GetTaskRunner()->PostTask([&]() {
done = true;
char thread_name[NAMELEN];
pthread_t current_thread = pthread_self();
pthread_getname_np(current_thread, thread_name, NAMELEN);
pthread_getschedparam(current_thread, &policy, &param);
ASSERT_EQ(thread_name, thread2_name);
ASSERT_EQ(policy, SCHED_OTHER);
ASSERT_EQ(param.sched_priority, 10);
});
thread.Join();
ASSERT_TRUE(done);
}
#endif
53 changes: 47 additions & 6 deletions shell/common/thread_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,73 @@

#include "flutter/shell/common/thread_host.h"

#include <algorithm>
#include <memory>
#include <string>
#include <utility>

namespace flutter {

std::string ThreadHost::ThreadName(Type type, std::string prefix) {
switch (type) {
case Type::Platform:
return prefix + ".platform";
case Type::UI:
return prefix + ".ui";
case Type::IO:
return prefix + ".io";
case Type::RASTER:
return prefix + ".raster";
case Type::Profiler:
return prefix + ".profiler";
}
}

std::unique_ptr<fml::Thread> ThreadHost::CreateThread(
Type type,
ThreadHost::ThreadConfig configure) {
std::string name = ThreadName(type, name_prefix);
if (configure != nullptr) {
return std::make_unique<fml::Thread>(std::move(configure));
}
return std::make_unique<fml::Thread>(
fml::Thread::ThreadConfig::DefaultConfigure(name));
}

ThreadHost::ThreadHost() = default;

ThreadHost::ThreadHost(ThreadHost&&) = default;

ThreadHost::ThreadHost(std::string name_prefix_arg, uint64_t mask)
ThreadHost::ThreadHost(std::string name_prefix_arg,
uint64_t mask,
ThreadHostConfig configure_host)
: name_prefix(name_prefix_arg) {
if (mask & ThreadHost::Type::Platform) {
platform_thread = std::make_unique<fml::Thread>(name_prefix + ".platform");
platform_thread =
CreateThread(ThreadHost::Type::Platform,
std::move(configure_host.platform_configure));
}

if (mask & ThreadHost::Type::UI) {
ui_thread = std::make_unique<fml::Thread>(name_prefix + ".ui");
ui_thread = CreateThread(ThreadHost::Type::UI,
std::move(configure_host.ui_configure));
}

if (mask & ThreadHost::Type::RASTER) {
raster_thread = std::make_unique<fml::Thread>(name_prefix + ".raster");
raster_thread = CreateThread(ThreadHost::Type::RASTER,
std::move(configure_host.raster_configure));
}

if (mask & ThreadHost::Type::IO) {
io_thread = std::make_unique<fml::Thread>(name_prefix + ".io");
io_thread = CreateThread(ThreadHost::Type::IO,
std::move(configure_host.io_configure));
}

if (mask & ThreadHost::Type::Profiler) {
profiler_thread = std::make_unique<fml::Thread>(name_prefix + ".profiler");
profiler_thread =
CreateThread(ThreadHost::Type::Profiler,
std::move(configure_host.profiler_configure));
;
}
}

Expand Down
21 changes: 20 additions & 1 deletion shell/common/thread_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_SHELL_COMMON_THREAD_HOST_H_

#include <memory>
#include <string>

#include "flutter/fml/macros.h"
#include "flutter/fml/thread.h"
Expand All @@ -22,6 +23,19 @@ struct ThreadHost {
Profiler = 1 << 4,
};

static std::string ThreadName(Type type, std::string prefix = "");

using ThreadConfig = std::unique_ptr<fml::Thread::ThreadConfig>;
/// The collection of all the thread configures, and we create custom thread
/// configure in engine to info the thread.
struct ThreadHostConfig {
ThreadConfig platform_configure;
ThreadConfig ui_configure;
ThreadConfig raster_configure;
ThreadConfig io_configure;
ThreadConfig profiler_configure;
};

std::string name_prefix;
std::unique_ptr<fml::Thread> platform_thread;
std::unique_ptr<fml::Thread> ui_thread;
Expand All @@ -35,9 +49,14 @@ struct ThreadHost {

ThreadHost& operator=(ThreadHost&&) = default;

ThreadHost(std::string name_prefix, uint64_t type_mask);
ThreadHost(std::string name_prefix,
uint64_t type_mask,
ThreadHostConfig configure_host = ThreadHostConfig());

~ThreadHost();

private:
std::unique_ptr<fml::Thread> CreateThread(Type type, ThreadConfig configure);
};

} // namespace flutter
Expand Down
Loading

0 comments on commit 78321b4

Please sign in to comment.