// Copyright 2020 yuzu Emulator Project // 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 #else #include #endif namespace Common { #ifdef _MSC_VER struct Fiber::FiberImpl { LPVOID handle = nullptr; }; void Fiber::start() { ASSERT(previous_fiber != nullptr); previous_fiber->guard.unlock(); previous_fiber.reset(); entry_point(start_parameter); UNREACHABLE(); } 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{} { 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 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; } ConvertFiberToThread(); guard.unlock(); } 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(); } 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 constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB struct alignas(64) Fiber::FiberImpl { std::array stack; boost::context::detail::fcontext_t context; }; void Fiber::start(boost::context::detail::transfer_t& transfer) { 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) { 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(); impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), FiberStartFunc); } Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { impl = std::make_unique(); } Fiber::~Fiber() { // Make sure the Fiber is not being used 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; } guard.unlock(); } 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; ASSERT(previous_fiber != nullptr); 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->guard.lock(); fiber->is_thread_fiber = true; return fiber; } #endif } // namespace Common