From bc266a9d98f38f6fd1006f1ca52bd57e6a7f37d3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 4 Feb 2020 15:06:23 -0400 Subject: Common: Implement a basic Fiber class. --- src/common/fiber.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/common/fiber.cpp (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp new file mode 100644 index 000000000..eb59f1aa9 --- /dev/null +++ b/src/common/fiber.cpp @@ -0,0 +1,147 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/fiber.h" + +namespace Common { + +#ifdef _MSC_VER +#include + +struct Fiber::FiberImpl { + LPVOID handle = nullptr; +}; + +void Fiber::_start([[maybe_unused]] void* parameter) { + guard.lock(); + if (previous_fiber) { + previous_fiber->guard.unlock(); + previous_fiber = nullptr; + } + entry_point(start_parameter); +} + +static void __stdcall FiberStartFunc(LPVOID lpFiberParameter) +{ + auto fiber = static_cast(lpFiberParameter); + fiber->_start(nullptr); +} + +Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + impl = std::make_unique(); + impl->handle = CreateFiber(0, &FiberStartFunc, this); +} + +Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { + impl = std::make_unique(); +} + +Fiber::~Fiber() { + // Make sure the Fiber is not being used + guard.lock(); + guard.unlock(); + DeleteFiber(impl->handle); +} + +void Fiber::Exit() { + if (!is_thread_fiber) { + return; + } + ConvertFiberToThread(); + guard.unlock(); +} + +void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + to->guard.lock(); + to->previous_fiber = from; + SwitchToFiber(to->impl->handle); + auto previous_fiber = from->previous_fiber; + if (previous_fiber) { + previous_fiber->guard.unlock(); + previous_fiber.reset(); + } +} + +std::shared_ptr Fiber::ThreadToFiber() { + std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->guard.lock(); + fiber->impl->handle = ConvertThreadToFiber(NULL); + fiber->is_thread_fiber = true; + return fiber; +} + +#else + +#include + +constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB + +struct Fiber::FiberImpl { + boost::context::detail::fcontext_t context; + std::array stack; +}; + +void Fiber::_start(void* parameter) { + guard.lock(); + boost::context::detail::transfer_t* transfer = static_cast(parameter); + if (previous_fiber) { + previous_fiber->impl->context = transfer->fctx; + previous_fiber->guard.unlock(); + previous_fiber = nullptr; + } + entry_point(start_parameter); +} + +static void FiberStartFunc(boost::context::detail::transfer_t transfer) +{ + auto fiber = static_cast(transfer.data); + fiber->_start(&transfer); +} + +Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + impl = std::make_unique(); + auto start_func = std::bind(&Fiber::start, this); + impl->context = + boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func); +} + +Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { + impl = std::make_unique(); +} + +Fiber::~Fiber() { + // Make sure the Fiber is not being used + guard.lock(); + guard.unlock(); +} + +void Fiber::Exit() { + if (!is_thread_fiber) { + return; + } + guard.unlock(); +} + +void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + to->guard.lock(); + to->previous_fiber = from; + auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); + auto previous_fiber = from->previous_fiber; + if (previous_fiber) { + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); + } +} + +std::shared_ptr Fiber::ThreadToFiber() { + std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->is_thread_fiber = true; + return fiber; +} + +#endif +} // namespace Common -- cgit v1.2.3 From 8d0e3c542258cc50081af93aa85e0e3cbf8900c3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 14:13:16 -0400 Subject: Tests: Add tests for fibers and refactor/fix Fiber class --- src/common/fiber.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index eb59f1aa9..a2c0401c4 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -3,18 +3,21 @@ // Refer to the license.txt file included. #include "common/fiber.h" +#ifdef _MSC_VER +#include +#else +#include +#endif namespace Common { #ifdef _MSC_VER -#include struct Fiber::FiberImpl { LPVOID handle = nullptr; }; -void Fiber::_start([[maybe_unused]] void* parameter) { - guard.lock(); +void Fiber::start() { if (previous_fiber) { previous_fiber->guard.unlock(); previous_fiber = nullptr; @@ -22,10 +25,10 @@ void Fiber::_start([[maybe_unused]] void* parameter) { entry_point(start_parameter); } -static void __stdcall FiberStartFunc(LPVOID lpFiberParameter) +void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { - auto fiber = static_cast(lpFiberParameter); - fiber->_start(nullptr); + auto fiber = static_cast(fiber_parameter); + fiber->start(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -74,30 +77,26 @@ std::shared_ptr Fiber::ThreadToFiber() { #else -#include - constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB -struct Fiber::FiberImpl { - boost::context::detail::fcontext_t context; +struct alignas(64) Fiber::FiberImpl { std::array stack; + boost::context::detail::fcontext_t context; }; -void Fiber::_start(void* parameter) { - guard.lock(); - boost::context::detail::transfer_t* transfer = static_cast(parameter); +void Fiber::start(boost::context::detail::transfer_t& transfer) { if (previous_fiber) { - previous_fiber->impl->context = transfer->fctx; + previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); previous_fiber = nullptr; } entry_point(start_parameter); } -static void FiberStartFunc(boost::context::detail::transfer_t transfer) +void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->_start(&transfer); + fiber->start(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -139,6 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { std::shared_ptr Fiber::ThreadToFiber() { std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->guard.lock(); fiber->is_thread_fiber = true; return fiber; } -- cgit v1.2.3 From be320a9e10fda32a984b12cdfe3aaf09cc67b39a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 15:48:20 -0400 Subject: Common: Polish Fiber class, add comments, asserts and more tests. --- src/common/fiber.cpp | 55 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a2c0401c4..a88a30ced 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/fiber.h" #ifdef _MSC_VER #include @@ -18,11 +19,11 @@ struct Fiber::FiberImpl { }; void Fiber::start() { - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) @@ -43,12 +44,16 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } DeleteFiber(impl->handle); } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -57,14 +62,15 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { @@ -85,12 +91,12 @@ struct alignas(64) Fiber::FiberImpl { }; void Fiber::start(boost::context::detail::transfer_t& transfer) { - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) @@ -113,11 +119,15 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -125,15 +135,16 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { -- cgit v1.2.3 From 03e4f5dac436fe361834e6b9918983e9c4787acb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 13:18:23 -0400 Subject: Common: Correct fcontext fibers. --- src/common/fiber.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a88a30ced..e91d86dbe 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,6 +12,7 @@ namespace Common { + #ifdef _MSC_VER struct Fiber::FiberImpl { @@ -82,7 +83,6 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else - constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB struct alignas(64) Fiber::FiberImpl { @@ -108,9 +108,8 @@ void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - auto start_func = std::bind(&Fiber::start, this); - impl->context = - boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func); + impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), + FiberStartFunc); } Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { @@ -139,7 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); + auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr); auto previous_fiber = from->previous_fiber; ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; -- cgit v1.2.3 From 1bd706344e2381e11245b2f0bdc291429e46c634 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 13:33:13 -0400 Subject: Common/Tests: Clang Format. --- src/common/fiber.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e91d86dbe..a46be73c1 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,7 +12,6 @@ namespace Common { - #ifdef _MSC_VER struct Fiber::FiberImpl { @@ -27,14 +26,14 @@ void Fiber::start() { UNREACHABLE(); } -void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) -{ - auto fiber = static_cast(fiber_parameter); - fiber->start(); +void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { + auto fiber = static_cast(fiber_parameter); + fiber->start(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, + previous_fiber{} { impl = std::make_unique(); impl->handle = CreateFiber(0, &FiberStartFunc, this); } @@ -99,14 +98,14 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } -void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) -{ - auto fiber = static_cast(transfer.data); - fiber->start(transfer); +void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { + auto fiber = static_cast(transfer.data); + fiber->start(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, + previous_fiber{} { impl = std::make_unique(); impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), FiberStartFunc); -- cgit v1.2.3 From 3398f701eeac63f3cfcf193f3e9c1ee2f06edb08 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 14:21:23 -0400 Subject: Common: Make MinGW build use Windows Fibers instead of fcontext_t --- src/common/fiber.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a46be73c1..050c93acb 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -4,7 +4,7 @@ #include "common/assert.h" #include "common/fiber.h" -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) #include #else #include @@ -12,7 +12,7 @@ namespace Common { -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) struct Fiber::FiberImpl { LPVOID handle = nullptr; -- cgit v1.2.3 From 1f7dd36499786d373b143a4437d4c32e077a32aa Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 14:45:08 -0400 Subject: Common/Tests: Address Feedback --- src/common/fiber.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 050c93acb..1220eddf0 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -32,13 +32,12 @@ void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, - previous_fiber{} { + : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); impl->handle = CreateFiber(0, &FiberStartFunc, this); } -Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { +Fiber::Fiber() { impl = std::make_unique(); } -- cgit v1.2.3 From 49a7e0984a1210832b8be24433a95711c7ce029b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 15:02:04 -0400 Subject: Core/HostTiming: Allow events to be advanced manually. --- src/common/fiber.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 1220eddf0..e9c0946b6 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -110,7 +110,7 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete FiberStartFunc); } -Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { +Fiber::Fiber() { impl = std::make_unique(); } -- cgit v1.2.3 From 7d2b1a6ec4a1c0daea0bac83a83c85f263609224 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 14:39:27 -0400 Subject: Common/Fiber: Correct f_context based Fibers. --- src/common/fiber.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e9c0946b6..3ef820c62 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -81,10 +81,10 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB +constexpr std::size_t default_stack_size = 1024 * 1024; // 4MB -struct alignas(64) Fiber::FiberImpl { - std::array stack; +struct Fiber::FiberImpl { + alignas(64) std::array stack; boost::context::detail::fcontext_t context; }; @@ -106,8 +106,10 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), - FiberStartFunc); + void* stack_start = + static_cast(static_cast(impl->stack.data()) + default_stack_size); + impl->context = + boost::context::detail::make_fcontext(stack_start, impl->stack.size(), FiberStartFunc); } Fiber::Fiber() { @@ -136,7 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr); + auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); auto previous_fiber = from->previous_fiber; ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; -- cgit v1.2.3 From 41013381d69f952f78b85de3ce226c1499d889b6 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 17:34:23 -0400 Subject: Common/Fiber: Additional corrections to f_context. --- src/common/fiber.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 3ef820c62..e4ecc73df 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -81,7 +81,7 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024; // 4MB +constexpr std::size_t default_stack_size = 1024 * 1024; // 1MB struct Fiber::FiberImpl { alignas(64) std::array stack; @@ -106,10 +106,10 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - void* stack_start = - static_cast(static_cast(impl->stack.data()) + default_stack_size); + u8* stack_limit = impl->stack.data(); + u8* stack_base = stack_limit + default_stack_size; impl->context = - boost::context::detail::make_fcontext(stack_start, impl->stack.size(), FiberStartFunc); + boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); } Fiber::Fiber() { -- cgit v1.2.3 From 137d862d9b275209b3d62a413396a15e9e14b4b4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 16:32:47 -0400 Subject: Common/Fiber: Implement Rewinding. --- src/common/fiber.cpp | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e4ecc73df..f61479e13 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,10 +12,13 @@ namespace Common { +constexpr std::size_t default_stack_size = 256 * 1024; // 256kb + #if defined(_WIN32) || defined(WIN32) struct Fiber::FiberImpl { LPVOID handle = nullptr; + LPVOID rewind_handle = nullptr; }; void Fiber::start() { @@ -26,15 +29,29 @@ void Fiber::start() { UNREACHABLE(); } +void Fiber::onRewind() { + ASSERT(impl->handle != nullptr); + DeleteFiber(impl->handle); + impl->handle = impl->rewind_handle; + impl->rewind_handle = nullptr; + rewind_point(rewind_parameter); + UNREACHABLE(); +} + void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); fiber->start(); } +void __stdcall Fiber::RewindStartFunc(void* fiber_parameter) { + auto fiber = static_cast(fiber_parameter); + fiber->onRewind(); +} + Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); - impl->handle = CreateFiber(0, &FiberStartFunc, this); + impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); } Fiber::Fiber() { @@ -60,6 +77,18 @@ void Fiber::Exit() { guard.unlock(); } +void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { + rewind_point = std::move(rewind_func); + rewind_parameter = start_parameter; +} + +void Fiber::Rewind() { + ASSERT(rewind_point); + ASSERT(impl->rewind_handle == nullptr); + impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this); + SwitchToFiber(impl->rewind_handle); +} + void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); @@ -81,7 +110,6 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024; // 1MB struct Fiber::FiberImpl { alignas(64) std::array stack; -- cgit v1.2.3 From b6655aa2e492e326e319b09e832c1612bf27acf4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 1 Apr 2020 09:19:10 -0400 Subject: Common/Fiber: Implement Rewind on Boost Context. --- src/common/fiber.cpp | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f61479e13..6ea314d75 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -113,7 +113,11 @@ std::shared_ptr Fiber::ThreadToFiber() { struct Fiber::FiberImpl { alignas(64) std::array stack; + u8* stack_limit; + alignas(64) std::array rewind_stack; + u8* rewind_stack_limit; boost::context::detail::fcontext_t context; + boost::context::detail::fcontext_t rewind_context; }; void Fiber::start(boost::context::detail::transfer_t& transfer) { @@ -125,21 +129,43 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } +void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transfer) { + ASSERT(impl->context != nullptr); + impl->context = impl->rewind_context; + impl->rewind_context = nullptr; + u8* tmp = impl->stack_limit; + impl->stack_limit = impl->rewind_stack_limit; + impl->rewind_stack_limit = tmp; + rewind_point(rewind_parameter); + UNREACHABLE(); +} + void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); fiber->start(transfer); } +void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { + auto fiber = static_cast(transfer.data); + fiber->onRewind(transfer); +} + Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - u8* stack_limit = impl->stack.data(); - u8* stack_base = stack_limit + default_stack_size; + impl->stack_limit = impl->stack.data(); + impl->rewind_stack_limit = impl->rewind_stack.data(); + u8* stack_base = impl->stack_limit + default_stack_size; impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); } +void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { + rewind_point = std::move(rewind_func); + rewind_parameter = start_parameter; +} + Fiber::Fiber() { impl = std::make_unique(); } @@ -161,6 +187,15 @@ void Fiber::Exit() { guard.unlock(); } +void Fiber::Rewind() { + ASSERT(rewind_point); + ASSERT(impl->rewind_context == nullptr); + u8* stack_base = impl->rewind_stack_limit + default_stack_size; + impl->rewind_context = + boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc); + boost::context::detail::jump_fcontext(impl->rewind_context, this); +} + void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); -- cgit v1.2.3 From e77ee67bfacf9a0d3b9e7cd164531a2be158adc9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 13 May 2020 13:49:36 -0400 Subject: Common/Fiber: Address Feedback and Correct Memory leaks. --- src/common/fiber.cpp | 58 +++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 26 deletions(-) (limited to 'src/common/fiber.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 6ea314d75..f97ad433b 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -21,7 +21,7 @@ struct Fiber::FiberImpl { LPVOID rewind_handle = nullptr; }; -void Fiber::start() { +void Fiber::Start() { ASSERT(previous_fiber != nullptr); previous_fiber->guard.unlock(); previous_fiber.reset(); @@ -29,7 +29,7 @@ void Fiber::start() { UNREACHABLE(); } -void Fiber::onRewind() { +void Fiber::OnRewind() { ASSERT(impl->handle != nullptr); DeleteFiber(impl->handle); impl->handle = impl->rewind_handle; @@ -38,14 +38,14 @@ void Fiber::onRewind() { UNREACHABLE(); } -void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { +void Fiber::FiberStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); - fiber->start(); + fiber->Start(); } -void __stdcall Fiber::RewindStartFunc(void* fiber_parameter) { +void Fiber::RewindStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); - fiber->onRewind(); + fiber->OnRewind(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -59,8 +59,11 @@ Fiber::Fiber() { } Fiber::~Fiber() { + if (released) { + return; + } // Make sure the Fiber is not being used - bool locked = guard.try_lock(); + const bool locked = guard.try_lock(); ASSERT_MSG(locked, "Destroying a fiber that's still running"); if (locked) { guard.unlock(); @@ -75,6 +78,7 @@ void Fiber::Exit() { } ConvertFiberToThread(); guard.unlock(); + released = true; } void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { @@ -89,22 +93,21 @@ void Fiber::Rewind() { SwitchToFiber(impl->rewind_handle); } -void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { +void Fiber::YieldTo(std::shared_ptr& from, std::shared_ptr& to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); - auto previous_fiber = from->previous_fiber; - ASSERT(previous_fiber != nullptr); - previous_fiber->guard.unlock(); - previous_fiber.reset(); + ASSERT(from->previous_fiber != nullptr); + from->previous_fiber->guard.unlock(); + from->previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { std::shared_ptr fiber = std::shared_ptr{new Fiber()}; fiber->guard.lock(); - fiber->impl->handle = ConvertThreadToFiber(NULL); + fiber->impl->handle = ConvertThreadToFiber(nullptr); fiber->is_thread_fiber = true; return fiber; } @@ -120,7 +123,7 @@ struct Fiber::FiberImpl { boost::context::detail::fcontext_t rewind_context; }; -void Fiber::start(boost::context::detail::transfer_t& transfer) { +void Fiber::Start(boost::context::detail::transfer_t& transfer) { ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); @@ -129,7 +132,7 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } -void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transfer) { +void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) { ASSERT(impl->context != nullptr); impl->context = impl->rewind_context; impl->rewind_context = nullptr; @@ -142,17 +145,16 @@ void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transf void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->start(transfer); + fiber->Start(transfer); } void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->onRewind(transfer); + fiber->OnRewind(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, - previous_fiber{} { + : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); impl->stack_limit = impl->stack.data(); impl->rewind_stack_limit = impl->rewind_stack.data(); @@ -171,8 +173,11 @@ Fiber::Fiber() { } Fiber::~Fiber() { + if (released) { + return; + } // Make sure the Fiber is not being used - bool locked = guard.try_lock(); + const bool locked = guard.try_lock(); ASSERT_MSG(locked, "Destroying a fiber that's still running"); if (locked) { guard.unlock(); @@ -180,11 +185,13 @@ Fiber::~Fiber() { } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } guard.unlock(); + released = true; } void Fiber::Rewind() { @@ -196,17 +203,16 @@ void Fiber::Rewind() { boost::context::detail::jump_fcontext(impl->rewind_context, this); } -void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { +void Fiber::YieldTo(std::shared_ptr& from, std::shared_ptr& to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); - auto previous_fiber = from->previous_fiber; - ASSERT(previous_fiber != nullptr); - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); + ASSERT(from->previous_fiber != nullptr); + from->previous_fiber->impl->context = transfer.fctx; + from->previous_fiber->guard.unlock(); + from->previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { -- cgit v1.2.3