common/fiber: make fibers easier to use

This commit is contained in:
Liam 2022-07-02 12:33:49 -04:00
parent 603952bc27
commit ed0319cfed
9 changed files with 79 additions and 170 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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();
} }

View File

@ -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();

View File

@ -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) {
{ {

View File

@ -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{};

View File

@ -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) {

View File

@ -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);

View File

@ -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();