blob: e2830485c0a89b7599f0fe3087728c04cd4e11df [file] [log] [blame]
// Copyright 2023 The Fuchsia 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 <lib/async/cpp/irq.h>
#include <lib/async/cpp/wait.h>
#include <lib/driver/testing/cpp/driver_runtime.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/fdf/cpp/env.h>
#include <lib/sync/cpp/completion.h>
#include <lib/zx/interrupt.h>
#include "src/devices/bin/driver_runtime/dispatcher.h"
#include "src/devices/bin/driver_runtime/driver_context.h"
#include "src/devices/bin/driver_runtime/runtime_test_case.h"
namespace driver_runtime {
extern DispatcherCoordinator& GetDispatcherCoordinator();
}
class ThreadPoolTest : public RuntimeTestCase {
public:
void SetUp() override;
void TearDown() override;
void InitTestDispatcher(std::string_view scheduler_role,
fdf::SynchronizedDispatcher::Options options = {});
driver_runtime::Dispatcher* runtime_dispatcher() {
return static_cast<driver_runtime::Dispatcher*>(dispatcher_.get());
}
protected:
fdf_testing::internal::DriverRuntimeEnv runtime_env;
fdf::SynchronizedDispatcher dispatcher_;
libsync::Completion shutdown_completion_;
};
void ThreadPoolTest::SetUp() {
// Make sure each test starts with exactly one thread.
driver_runtime::GetDispatcherCoordinator().Reset();
ASSERT_EQ(ZX_OK, driver_runtime::GetDispatcherCoordinator().Start());
}
void ThreadPoolTest::TearDown() {
if (dispatcher_.get() != nullptr) {
dispatcher_.ShutdownAsync();
shutdown_completion_.Wait();
}
}
void ThreadPoolTest::InitTestDispatcher(std::string_view scheduler_role,
fdf::SynchronizedDispatcher::Options options) {
ZX_ASSERT(dispatcher_.get() == nullptr);
auto fake_driver = CreateFakeDriver();
auto shutdown_handler = [&](fdf_dispatcher_t* dispatcher) { shutdown_completion_.Signal(); };
auto dispatcher = fdf_env::DispatcherBuilder::CreateSynchronizedWithOwner(
fake_driver, options, "dispatcher", shutdown_handler, scheduler_role);
ASSERT_FALSE(dispatcher.is_error());
dispatcher_ = *std::move(dispatcher);
}
TEST_F(ThreadPoolTest, NoSchedulerRole) {
constexpr std::string_view kSchedulerRole = "";
ASSERT_NO_FATAL_FAILURE(InitTestDispatcher(kSchedulerRole));
libsync::Completion task_completion;
ASSERT_OK(async::PostTask(dispatcher_.async_dispatcher(), [&] {
auto role_profile_status = driver_context::GetRoleProfileStatus();
ASSERT_FALSE(role_profile_status.has_value());
task_completion.Signal();
}));
task_completion.Wait();
}
TEST_F(ThreadPoolTest, WithSchedulerRole) {
constexpr std::string_view kSchedulerRole = "fuchsia.default";
ASSERT_NO_FATAL_FAILURE(InitTestDispatcher(kSchedulerRole));
libsync::Completion task_completion;
ASSERT_OK(async::PostTask(dispatcher_.async_dispatcher(), [&] {
auto role_profile_status = driver_context::GetRoleProfileStatus();
ASSERT_TRUE(role_profile_status.has_value());
ASSERT_OK(role_profile_status.value());
task_completion.Signal();
}));
task_completion.Wait();
}
TEST_F(ThreadPoolTest, BadSchedulerRole) {
constexpr std::string_view kSchedulerRole = "fuchsia.test-role:not-found";
ASSERT_NO_FATAL_FAILURE(InitTestDispatcher(kSchedulerRole));
libsync::Completion task_completion;
ASSERT_OK(async::PostTask(dispatcher_.async_dispatcher(), [&] {
auto role_profile_status = driver_context::GetRoleProfileStatus();
ASSERT_TRUE(role_profile_status.has_value());
ASSERT_NE(ZX_OK, *role_profile_status);
task_completion.Signal();
}));
task_completion.Wait();
}
TEST_F(ThreadPoolTest, AllowSyncCalls) {
constexpr std::string_view kSchedulerRole = "fuchsia.default";
ASSERT_NO_FATAL_FAILURE(
InitTestDispatcher(kSchedulerRole, fdf::SynchronizedDispatcher::Options::kAllowSyncCalls));
libsync::Completion task_completion;
ASSERT_OK(async::PostTask(dispatcher_.async_dispatcher(), [&] {
auto role_profile_status = driver_context::GetRoleProfileStatus();
ASSERT_TRUE(role_profile_status.has_value());
ASSERT_OK(role_profile_status.value());
auto thread_pool = runtime_dispatcher()->thread_pool();
ASSERT_EQ(thread_pool->scheduler_role(), kSchedulerRole);
ASSERT_EQ(thread_pool->num_threads(), 1u);
ASSERT_EQ(driver_runtime::GetDispatcherCoordinator().default_thread_pool()->num_threads(), 1u);
task_completion.Signal();
}));
task_completion.Wait();
}
TEST_F(ThreadPoolTest, Shutdown) {
constexpr std::string_view kSchedulerRole = "fuchsia.default";
auto fake_driver = CreateFakeDriver();
libsync::Completion shutdown_completion;
auto shutdown_handler = [&](fdf_dispatcher_t* dispatcher) {
auto role_profile_status = driver_context::GetRoleProfileStatus();
ASSERT_TRUE(role_profile_status.has_value());
ASSERT_OK(role_profile_status.value());
shutdown_completion.Signal();
};
auto dispatcher = fdf_env::DispatcherBuilder::CreateSynchronizedWithOwner(
fake_driver, {}, "dispatcher", shutdown_handler, kSchedulerRole);
ASSERT_FALSE(dispatcher.is_error());
dispatcher->ShutdownAsync();
shutdown_completion.Wait();
}
class MultipleDispatchersThreadPoolTest : public RuntimeTestCase {
public:
void TearDown() override;
zx::result<fdf::Unowned<fdf::SynchronizedDispatcher>> CreateTestDispatcher(
std::string_view scheduler_role, fdf::SynchronizedDispatcher::Options options);
driver_runtime::Dispatcher::ThreadPool* GetThreadPool(
fdf::Unowned<fdf::SynchronizedDispatcher>& dispatcher) {
return static_cast<driver_runtime::Dispatcher*>(dispatcher->get())->thread_pool();
}
protected:
std::vector<fdf::SynchronizedDispatcher> dispatchers_;
libsync::Completion shutdown_completion_;
fbl::Mutex lock_;
uint32_t num_dispatchers_shutdown_ __TA_GUARDED(&lock_) = 0;
};
void MultipleDispatchersThreadPoolTest::TearDown() {
for (auto& dispatcher : dispatchers_) {
dispatcher.ShutdownAsync();
}
if (!dispatchers_.empty()) {
shutdown_completion_.Wait();
}
}
zx::result<fdf::Unowned<fdf::SynchronizedDispatcher>>
MultipleDispatchersThreadPoolTest::CreateTestDispatcher(
std::string_view scheduler_role, fdf::SynchronizedDispatcher::Options options) {
auto fake_driver = CreateFakeDriver();
auto shutdown_handler = [&](fdf_dispatcher_t* dispatcher) {
bool signal = false;
{
fbl::AutoLock lock(&lock_);
num_dispatchers_shutdown_++;
if (num_dispatchers_shutdown_ == dispatchers_.size()) {
signal = true;
}
}
if (signal) {
shutdown_completion_.Signal();
}
};
auto dispatcher = fdf_env::DispatcherBuilder::CreateSynchronizedWithOwner(
fake_driver, options, "dispatcher", shutdown_handler, scheduler_role);
if (dispatcher.is_error()) {
}
fdf::Unowned<fdf::SynchronizedDispatcher> unowned = dispatcher->borrow();
dispatchers_.push_back(*std::move(dispatcher));
return zx::ok(unowned);
}
TEST_F(MultipleDispatchersThreadPoolTest, ManyDispatchers) {
constexpr std::string_view kSchedulerRole = "fuchsia.default";
constexpr std::string_view kBadSchedulerRole = "fuchsia.test-role:not-found";
constexpr uint32_t kNumDispatchers = 5;
driver_runtime::Dispatcher::ThreadPool* want_thread_pool = nullptr;
for (uint32_t i = 0; i < kNumDispatchers; i++) {
auto dispatcher = CreateTestDispatcher(kSchedulerRole, {});
ASSERT_OK(dispatcher.status_value());
auto thread_pool = GetThreadPool(*dispatcher);
if (want_thread_pool == nullptr) {
want_thread_pool = thread_pool;
} else {
ASSERT_EQ(want_thread_pool, thread_pool);
}
libsync::Completion task_completion;
ASSERT_OK(async::PostTask(dispatcher->async_dispatcher(), [&] {
auto role_profile_status = driver_context::GetRoleProfileStatus();
ASSERT_TRUE(role_profile_status.has_value());
ASSERT_OK(role_profile_status.value());
task_completion.Signal();
}));
task_completion.Wait();
}
{
auto dispatcher = CreateTestDispatcher(kBadSchedulerRole, {});
ASSERT_OK(dispatcher.status_value());
auto thread_pool = GetThreadPool(*dispatcher);
ASSERT_NE(want_thread_pool, thread_pool);
}
}
TEST_F(MultipleDispatchersThreadPoolTest, NumThreads) {
constexpr std::string_view kSchedulerRole = "fuchsia.default";
{
auto dispatcher = CreateTestDispatcher(kSchedulerRole, {});
ASSERT_OK(dispatcher.status_value());
ASSERT_EQ(1u, GetThreadPool(*dispatcher)->num_threads());
}
{
auto dispatcher = CreateTestDispatcher(kSchedulerRole, {});
ASSERT_OK(dispatcher.status_value());
ASSERT_EQ(1u, GetThreadPool(*dispatcher)->num_threads());
}
{
auto dispatcher =
CreateTestDispatcher(kSchedulerRole, fdf::SynchronizedDispatcher::Options::kAllowSyncCalls);
ASSERT_OK(dispatcher.status_value());
ASSERT_EQ(2u, GetThreadPool(*dispatcher)->num_threads());
}
}
TEST_F(MultipleDispatchersThreadPoolTest, NumThreadsAllowSyncCalls) {
constexpr std::string_view kSchedulerRole = "fuchsia.default";
{
auto dispatcher =
CreateTestDispatcher(kSchedulerRole, fdf::SynchronizedDispatcher::Options::kAllowSyncCalls);
ASSERT_OK(dispatcher.status_value());
ASSERT_EQ(1u, GetThreadPool(*dispatcher)->num_threads());
}
{
auto dispatcher =
CreateTestDispatcher(kSchedulerRole, fdf::SynchronizedDispatcher::Options::kAllowSyncCalls);
ASSERT_OK(dispatcher.status_value());
ASSERT_EQ(2u, GetThreadPool(*dispatcher)->num_threads());
}
{
auto dispatcher = CreateTestDispatcher(kSchedulerRole, {});
ASSERT_OK(dispatcher.status_value());
ASSERT_EQ(2u, GetThreadPool(*dispatcher)->num_threads());
}
}