forked from suyu/suyu
common/fiber: make fibers easier to use
This commit is contained in:
parent
603952bc27
commit
ed0319cfed
9 changed files with 79 additions and 170 deletions
|
@ -20,10 +20,8 @@ struct Fiber::FiberImpl {
|
||||||
VirtualBuffer<u8> rewind_stack;
|
VirtualBuffer<u8> rewind_stack;
|
||||||
|
|
||||||
std::mutex guard;
|
std::mutex guard;
|
||||||
std::function<void(void*)> entry_point;
|
std::function<void()> entry_point;
|
||||||
std::function<void(void*)> rewind_point;
|
std::function<void()> rewind_point;
|
||||||
void* rewind_parameter{};
|
|
||||||
void* start_parameter{};
|
|
||||||
std::shared_ptr<Fiber> previous_fiber;
|
std::shared_ptr<Fiber> previous_fiber;
|
||||||
bool is_thread_fiber{};
|
bool is_thread_fiber{};
|
||||||
bool released{};
|
bool released{};
|
||||||
|
@ -34,13 +32,8 @@ struct Fiber::FiberImpl {
|
||||||
boost::context::detail::fcontext_t rewind_context{};
|
boost::context::detail::fcontext_t rewind_context{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void Fiber::SetStartParameter(void* new_parameter) {
|
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
|
||||||
impl->start_parameter = new_parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
|
|
||||||
impl->rewind_point = std::move(rewind_func);
|
impl->rewind_point = std::move(rewind_func);
|
||||||
impl->rewind_parameter = rewind_param;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||||
|
@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||||
impl->previous_fiber->impl->context = transfer.fctx;
|
impl->previous_fiber->impl->context = transfer.fctx;
|
||||||
impl->previous_fiber->impl->guard.unlock();
|
impl->previous_fiber->impl->guard.unlock();
|
||||||
impl->previous_fiber.reset();
|
impl->previous_fiber.reset();
|
||||||
impl->entry_point(impl->start_parameter);
|
impl->entry_point();
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
|
||||||
u8* tmp = impl->stack_limit;
|
u8* tmp = impl->stack_limit;
|
||||||
impl->stack_limit = impl->rewind_stack_limit;
|
impl->stack_limit = impl->rewind_stack_limit;
|
||||||
impl->rewind_stack_limit = tmp;
|
impl->rewind_stack_limit = tmp;
|
||||||
impl->rewind_point(impl->rewind_parameter);
|
impl->rewind_point();
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
|
||||||
fiber->OnRewind(transfer);
|
fiber->OnRewind(transfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
|
||||||
: impl{std::make_unique<FiberImpl>()} {
|
|
||||||
impl->entry_point = std::move(entry_point_func);
|
impl->entry_point = std::move(entry_point_func);
|
||||||
impl->start_parameter = start_parameter;
|
|
||||||
impl->stack_limit = impl->stack.data();
|
impl->stack_limit = impl->stack.data();
|
||||||
impl->rewind_stack_limit = impl->rewind_stack.data();
|
impl->rewind_stack_limit = impl->rewind_stack.data();
|
||||||
u8* stack_base = impl->stack_limit + default_stack_size;
|
u8* stack_base = impl->stack_limit + default_stack_size;
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace Common {
|
||||||
*/
|
*/
|
||||||
class Fiber {
|
class Fiber {
|
||||||
public:
|
public:
|
||||||
Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter);
|
Fiber(std::function<void()>&& entry_point_func);
|
||||||
~Fiber();
|
~Fiber();
|
||||||
|
|
||||||
Fiber(const Fiber&) = delete;
|
Fiber(const Fiber&) = delete;
|
||||||
|
@ -43,16 +43,13 @@ public:
|
||||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||||
|
|
||||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
|
void SetRewindPoint(std::function<void()>&& rewind_func);
|
||||||
|
|
||||||
void Rewind();
|
void Rewind();
|
||||||
|
|
||||||
/// Only call from main thread's fiber
|
/// Only call from main thread's fiber
|
||||||
void Exit();
|
void Exit();
|
||||||
|
|
||||||
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
|
||||||
void SetStartParameter(void* new_parameter);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Fiber();
|
Fiber();
|
||||||
|
|
||||||
|
|
|
@ -41,51 +41,32 @@ void CpuManager::Shutdown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
|
void CpuManager::GuestThreadFunction() {
|
||||||
return GuestThreadFunction;
|
if (is_multicore) {
|
||||||
}
|
MultiCoreRunGuestThread();
|
||||||
|
|
||||||
std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
|
|
||||||
return IdleThreadFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() {
|
|
||||||
return ShutdownThreadFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CpuManager::GuestThreadFunction(void* cpu_manager_) {
|
|
||||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
|
||||||
if (cpu_manager->is_multicore) {
|
|
||||||
cpu_manager->MultiCoreRunGuestThread();
|
|
||||||
} else {
|
} else {
|
||||||
cpu_manager->SingleCoreRunGuestThread();
|
SingleCoreRunGuestThread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::GuestRewindFunction(void* cpu_manager_) {
|
void CpuManager::GuestRewindFunction() {
|
||||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
if (is_multicore) {
|
||||||
if (cpu_manager->is_multicore) {
|
MultiCoreRunGuestLoop();
|
||||||
cpu_manager->MultiCoreRunGuestLoop();
|
|
||||||
} else {
|
} else {
|
||||||
cpu_manager->SingleCoreRunGuestLoop();
|
SingleCoreRunGuestLoop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::IdleThreadFunction(void* cpu_manager_) {
|
void CpuManager::IdleThreadFunction() {
|
||||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
if (is_multicore) {
|
||||||
if (cpu_manager->is_multicore) {
|
MultiCoreRunIdleThread();
|
||||||
cpu_manager->MultiCoreRunIdleThread();
|
|
||||||
} else {
|
} else {
|
||||||
cpu_manager->SingleCoreRunIdleThread();
|
SingleCoreRunIdleThread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::ShutdownThreadFunction(void* cpu_manager) {
|
void CpuManager::ShutdownThreadFunction() {
|
||||||
static_cast<CpuManager*>(cpu_manager)->ShutdownThread();
|
ShutdownThread();
|
||||||
}
|
|
||||||
|
|
||||||
void* CpuManager::GetStartFuncParameter() {
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -97,7 +78,7 @@ void CpuManager::MultiCoreRunGuestThread() {
|
||||||
kernel.CurrentScheduler()->OnThreadStart();
|
kernel.CurrentScheduler()->OnThreadStart();
|
||||||
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
||||||
auto& host_context = thread->GetHostContext();
|
auto& host_context = thread->GetHostContext();
|
||||||
host_context->SetRewindPoint(GuestRewindFunction, this);
|
host_context->SetRewindPoint([this] { GuestRewindFunction(); });
|
||||||
MultiCoreRunGuestLoop();
|
MultiCoreRunGuestLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +115,7 @@ void CpuManager::SingleCoreRunGuestThread() {
|
||||||
kernel.CurrentScheduler()->OnThreadStart();
|
kernel.CurrentScheduler()->OnThreadStart();
|
||||||
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
||||||
auto& host_context = thread->GetHostContext();
|
auto& host_context = thread->GetHostContext();
|
||||||
host_context->SetRewindPoint(GuestRewindFunction, this);
|
host_context->SetRewindPoint([this] { GuestRewindFunction(); });
|
||||||
SingleCoreRunGuestLoop();
|
SingleCoreRunGuestLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,10 +50,15 @@ public:
|
||||||
void Initialize();
|
void Initialize();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
static std::function<void(void*)> GetGuestThreadStartFunc();
|
std::function<void()> GetGuestThreadStartFunc() {
|
||||||
static std::function<void(void*)> GetIdleThreadStartFunc();
|
return [this] { GuestThreadFunction(); };
|
||||||
static std::function<void(void*)> GetShutdownThreadStartFunc();
|
}
|
||||||
void* GetStartFuncParameter();
|
std::function<void()> GetIdleThreadStartFunc() {
|
||||||
|
return [this] { IdleThreadFunction(); };
|
||||||
|
}
|
||||||
|
std::function<void()> GetShutdownThreadStartFunc() {
|
||||||
|
return [this] { ShutdownThreadFunction(); };
|
||||||
|
}
|
||||||
|
|
||||||
void PreemptSingleCore(bool from_running_enviroment = true);
|
void PreemptSingleCore(bool from_running_enviroment = true);
|
||||||
|
|
||||||
|
@ -62,10 +67,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void GuestThreadFunction(void* cpu_manager);
|
void GuestThreadFunction();
|
||||||
static void GuestRewindFunction(void* cpu_manager);
|
void GuestRewindFunction();
|
||||||
static void IdleThreadFunction(void* cpu_manager);
|
void IdleThreadFunction();
|
||||||
static void ShutdownThreadFunction(void* cpu_manager);
|
void ShutdownThreadFunction();
|
||||||
|
|
||||||
void MultiCoreRunGuestThread();
|
void MultiCoreRunGuestThread();
|
||||||
void MultiCoreRunGuestLoop();
|
void MultiCoreRunGuestLoop();
|
||||||
|
|
|
@ -622,7 +622,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
|
KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
|
||||||
switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
|
switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); });
|
||||||
state.needs_scheduling.store(true);
|
state.needs_scheduling.store(true);
|
||||||
state.interrupt_task_thread_runnable = false;
|
state.interrupt_task_thread_runnable = false;
|
||||||
state.should_count_idle = false;
|
state.should_count_idle = false;
|
||||||
|
@ -778,11 +778,6 @@ void KScheduler::ScheduleImpl() {
|
||||||
next_scheduler.SwitchContextStep2();
|
next_scheduler.SwitchContextStep2();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KScheduler::OnSwitch(void* this_scheduler) {
|
|
||||||
KScheduler* sched = static_cast<KScheduler*>(this_scheduler);
|
|
||||||
sched->SwitchToCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KScheduler::SwitchToCurrent() {
|
void KScheduler::SwitchToCurrent() {
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
|
|
|
@ -165,7 +165,6 @@ private:
|
||||||
*/
|
*/
|
||||||
void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
|
void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
|
||||||
|
|
||||||
static void OnSwitch(void* this_scheduler);
|
|
||||||
void SwitchToCurrent();
|
void SwitchToCurrent();
|
||||||
|
|
||||||
KThread* prev_thread{};
|
KThread* prev_thread{};
|
||||||
|
|
|
@ -246,14 +246,12 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack
|
||||||
|
|
||||||
Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
|
Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
|
||||||
VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
|
VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
|
||||||
ThreadType type, std::function<void(void*)>&& init_func,
|
ThreadType type, std::function<void()>&& init_func) {
|
||||||
void* init_func_parameter) {
|
|
||||||
// Initialize the thread.
|
// Initialize the thread.
|
||||||
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
|
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
|
||||||
|
|
||||||
// Initialize emulation parameters.
|
// Initialize emulation parameters.
|
||||||
thread->host_context =
|
thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func));
|
||||||
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
|
|
||||||
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
|
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
@ -265,15 +263,13 @@ Result KThread::InitializeDummyThread(KThread* thread) {
|
||||||
|
|
||||||
Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
|
Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
|
||||||
return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
|
return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
|
||||||
Core::CpuManager::GetIdleThreadStartFunc(),
|
system.GetCpuManager().GetIdleThreadStartFunc());
|
||||||
system.GetCpuManager().GetStartFuncParameter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
|
Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
|
||||||
KThreadFunction func, uintptr_t arg, s32 virt_core) {
|
KThreadFunction func, uintptr_t arg, s32 virt_core) {
|
||||||
return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
|
return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
|
||||||
Core::CpuManager::GetShutdownThreadStartFunc(),
|
system.GetCpuManager().GetShutdownThreadStartFunc());
|
||||||
system.GetCpuManager().GetStartFuncParameter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
|
Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
|
||||||
|
@ -281,8 +277,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr
|
||||||
KProcess* owner) {
|
KProcess* owner) {
|
||||||
system.Kernel().GlobalSchedulerContext().AddThread(thread);
|
system.Kernel().GlobalSchedulerContext().AddThread(thread);
|
||||||
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
|
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
|
||||||
ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(),
|
ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc());
|
||||||
system.GetCpuManager().GetStartFuncParameter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KThread::PostDestroy(uintptr_t arg) {
|
void KThread::PostDestroy(uintptr_t arg) {
|
||||||
|
|
|
@ -729,8 +729,7 @@ private:
|
||||||
[[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func,
|
[[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func,
|
||||||
uintptr_t arg, VAddr user_stack_top, s32 prio,
|
uintptr_t arg, VAddr user_stack_top, s32 prio,
|
||||||
s32 core, KProcess* owner, ThreadType type,
|
s32 core, KProcess* owner, ThreadType type,
|
||||||
std::function<void(void*)>&& init_func,
|
std::function<void()>&& init_func);
|
||||||
void* init_func_parameter);
|
|
||||||
|
|
||||||
static void RestorePriority(KernelCore& kernel_ctx, KThread* thread);
|
static void RestorePriority(KernelCore& kernel_ctx, KThread* thread);
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,15 @@ class TestControl1 {
|
||||||
public:
|
public:
|
||||||
TestControl1() = default;
|
TestControl1() = default;
|
||||||
|
|
||||||
void DoWork();
|
void DoWork() {
|
||||||
|
const u32 id = thread_ids.Get();
|
||||||
|
u32 value = items[id];
|
||||||
|
for (u32 i = 0; i < id; i++) {
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
results[id] = value;
|
||||||
|
Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
|
||||||
|
}
|
||||||
|
|
||||||
void ExecuteThread(u32 id);
|
void ExecuteThread(u32 id);
|
||||||
|
|
||||||
|
@ -54,35 +62,16 @@ public:
|
||||||
std::vector<u32> results;
|
std::vector<u32> results;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void WorkControl1(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl1*>(control);
|
|
||||||
test_control->DoWork();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestControl1::DoWork() {
|
|
||||||
const u32 id = thread_ids.Get();
|
|
||||||
u32 value = items[id];
|
|
||||||
for (u32 i = 0; i < id; i++) {
|
|
||||||
value++;
|
|
||||||
}
|
|
||||||
results[id] = value;
|
|
||||||
Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestControl1::ExecuteThread(u32 id) {
|
void TestControl1::ExecuteThread(u32 id) {
|
||||||
thread_ids.Register(id);
|
thread_ids.Register(id);
|
||||||
auto thread_fiber = Fiber::ThreadToFiber();
|
auto thread_fiber = Fiber::ThreadToFiber();
|
||||||
thread_fibers[id] = thread_fiber;
|
thread_fibers[id] = thread_fiber;
|
||||||
work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
|
work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); });
|
||||||
items[id] = rand() % 256;
|
items[id] = rand() % 256;
|
||||||
Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
|
Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
|
||||||
thread_fibers[id]->Exit();
|
thread_fibers[id]->Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ThreadStart1(u32 id, TestControl1& test_control) {
|
|
||||||
test_control.ExecuteThread(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This test checks for fiber setup configuration and validates that fibers are
|
/** This test checks for fiber setup configuration and validates that fibers are
|
||||||
* doing all the work required.
|
* doing all the work required.
|
||||||
*/
|
*/
|
||||||
|
@ -95,7 +84,7 @@ TEST_CASE("Fibers::Setup", "[common]") {
|
||||||
test_control.results.resize(num_threads, 0);
|
test_control.results.resize(num_threads, 0);
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
for (u32 i = 0; i < num_threads; i++) {
|
for (u32 i = 0; i < num_threads; i++) {
|
||||||
threads.emplace_back(ThreadStart1, i, std::ref(test_control));
|
threads.emplace_back([&test_control, i] { test_control.ExecuteThread(i); });
|
||||||
}
|
}
|
||||||
for (u32 i = 0; i < num_threads; i++) {
|
for (u32 i = 0; i < num_threads; i++) {
|
||||||
threads[i].join();
|
threads[i].join();
|
||||||
|
@ -167,21 +156,6 @@ public:
|
||||||
std::shared_ptr<Common::Fiber> fiber3;
|
std::shared_ptr<Common::Fiber> fiber3;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void WorkControl2_1(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl2*>(control);
|
|
||||||
test_control->DoWork1();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WorkControl2_2(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl2*>(control);
|
|
||||||
test_control->DoWork2();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WorkControl2_3(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl2*>(control);
|
|
||||||
test_control->DoWork3();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestControl2::ExecuteThread(u32 id) {
|
void TestControl2::ExecuteThread(u32 id) {
|
||||||
thread_ids.Register(id);
|
thread_ids.Register(id);
|
||||||
auto thread_fiber = Fiber::ThreadToFiber();
|
auto thread_fiber = Fiber::ThreadToFiber();
|
||||||
|
@ -193,18 +167,6 @@ void TestControl2::Exit() {
|
||||||
thread_fibers[id]->Exit();
|
thread_fibers[id]->Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ThreadStart2_1(u32 id, TestControl2& test_control) {
|
|
||||||
test_control.ExecuteThread(id);
|
|
||||||
test_control.CallFiber1();
|
|
||||||
test_control.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ThreadStart2_2(u32 id, TestControl2& test_control) {
|
|
||||||
test_control.ExecuteThread(id);
|
|
||||||
test_control.CallFiber2();
|
|
||||||
test_control.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This test checks for fiber thread exchange configuration and validates that fibers are
|
/** This test checks for fiber thread exchange configuration and validates that fibers are
|
||||||
* that a fiber has been successfully transferred from one thread to another and that the TLS
|
* that a fiber has been successfully transferred from one thread to another and that the TLS
|
||||||
* region of the thread is kept while changing fibers.
|
* region of the thread is kept while changing fibers.
|
||||||
|
@ -212,14 +174,19 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) {
|
||||||
TEST_CASE("Fibers::InterExchange", "[common]") {
|
TEST_CASE("Fibers::InterExchange", "[common]") {
|
||||||
TestControl2 test_control{};
|
TestControl2 test_control{};
|
||||||
test_control.thread_fibers.resize(2);
|
test_control.thread_fibers.resize(2);
|
||||||
test_control.fiber1 =
|
test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); });
|
||||||
std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control);
|
test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); });
|
||||||
test_control.fiber2 =
|
test_control.fiber3 = std::make_shared<Fiber>([&test_control] { test_control.DoWork3(); });
|
||||||
std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control);
|
std::thread thread1{[&test_control] {
|
||||||
test_control.fiber3 =
|
test_control.ExecuteThread(0);
|
||||||
std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control);
|
test_control.CallFiber1();
|
||||||
std::thread thread1(ThreadStart2_1, 0, std::ref(test_control));
|
test_control.Exit();
|
||||||
std::thread thread2(ThreadStart2_2, 1, std::ref(test_control));
|
}};
|
||||||
|
std::thread thread2{[&test_control] {
|
||||||
|
test_control.ExecuteThread(1);
|
||||||
|
test_control.CallFiber2();
|
||||||
|
test_control.Exit();
|
||||||
|
}};
|
||||||
thread1.join();
|
thread1.join();
|
||||||
thread2.join();
|
thread2.join();
|
||||||
REQUIRE(test_control.assert1);
|
REQUIRE(test_control.assert1);
|
||||||
|
@ -270,16 +237,6 @@ public:
|
||||||
std::shared_ptr<Common::Fiber> fiber2;
|
std::shared_ptr<Common::Fiber> fiber2;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void WorkControl3_1(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl3*>(control);
|
|
||||||
test_control->DoWork1();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WorkControl3_2(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl3*>(control);
|
|
||||||
test_control->DoWork2();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestControl3::ExecuteThread(u32 id) {
|
void TestControl3::ExecuteThread(u32 id) {
|
||||||
thread_ids.Register(id);
|
thread_ids.Register(id);
|
||||||
auto thread_fiber = Fiber::ThreadToFiber();
|
auto thread_fiber = Fiber::ThreadToFiber();
|
||||||
|
@ -291,12 +248,6 @@ void TestControl3::Exit() {
|
||||||
thread_fibers[id]->Exit();
|
thread_fibers[id]->Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ThreadStart3(u32 id, TestControl3& test_control) {
|
|
||||||
test_control.ExecuteThread(id);
|
|
||||||
test_control.CallFiber1();
|
|
||||||
test_control.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This test checks for one two threads racing for starting the same fiber.
|
/** This test checks for one two threads racing for starting the same fiber.
|
||||||
* It checks execution occurred in an ordered manner and by no time there were
|
* It checks execution occurred in an ordered manner and by no time there were
|
||||||
* two contexts at the same time.
|
* two contexts at the same time.
|
||||||
|
@ -304,12 +255,15 @@ static void ThreadStart3(u32 id, TestControl3& test_control) {
|
||||||
TEST_CASE("Fibers::StartRace", "[common]") {
|
TEST_CASE("Fibers::StartRace", "[common]") {
|
||||||
TestControl3 test_control{};
|
TestControl3 test_control{};
|
||||||
test_control.thread_fibers.resize(2);
|
test_control.thread_fibers.resize(2);
|
||||||
test_control.fiber1 =
|
test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); });
|
||||||
std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control);
|
test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); });
|
||||||
test_control.fiber2 =
|
const auto race_function{[&test_control](u32 id) {
|
||||||
std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control);
|
test_control.ExecuteThread(id);
|
||||||
std::thread thread1(ThreadStart3, 0, std::ref(test_control));
|
test_control.CallFiber1();
|
||||||
std::thread thread2(ThreadStart3, 1, std::ref(test_control));
|
test_control.Exit();
|
||||||
|
}};
|
||||||
|
std::thread thread1([&] { race_function(0); });
|
||||||
|
std::thread thread2([&] { race_function(1); });
|
||||||
thread1.join();
|
thread1.join();
|
||||||
thread2.join();
|
thread2.join();
|
||||||
REQUIRE(test_control.value1 == 1);
|
REQUIRE(test_control.value1 == 1);
|
||||||
|
@ -319,12 +273,10 @@ TEST_CASE("Fibers::StartRace", "[common]") {
|
||||||
|
|
||||||
class TestControl4;
|
class TestControl4;
|
||||||
|
|
||||||
static void WorkControl4(void* control);
|
|
||||||
|
|
||||||
class TestControl4 {
|
class TestControl4 {
|
||||||
public:
|
public:
|
||||||
TestControl4() {
|
TestControl4() {
|
||||||
fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this);
|
fiber1 = std::make_shared<Fiber>([this] { DoWork(); });
|
||||||
goal_reached = false;
|
goal_reached = false;
|
||||||
rewinded = false;
|
rewinded = false;
|
||||||
}
|
}
|
||||||
|
@ -336,7 +288,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoWork() {
|
void DoWork() {
|
||||||
fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);
|
fiber1->SetRewindPoint([this] { DoWork(); });
|
||||||
if (rewinded) {
|
if (rewinded) {
|
||||||
goal_reached = true;
|
goal_reached = true;
|
||||||
Fiber::YieldTo(fiber1, *thread_fiber);
|
Fiber::YieldTo(fiber1, *thread_fiber);
|
||||||
|
@ -351,11 +303,6 @@ public:
|
||||||
bool rewinded;
|
bool rewinded;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void WorkControl4(void* control) {
|
|
||||||
auto* test_control = static_cast<TestControl4*>(control);
|
|
||||||
test_control->DoWork();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Fibers::Rewind", "[common]") {
|
TEST_CASE("Fibers::Rewind", "[common]") {
|
||||||
TestControl4 test_control{};
|
TestControl4 test_control{};
|
||||||
test_control.Execute();
|
test_control.Execute();
|
||||||
|
|
Loading…
Reference in a new issue