// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #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() { if (previous_fiber) { previous_fiber->guard.unlock(); previous_fiber = nullptr; } entry_point(start_parameter); } 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 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 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) { if (previous_fiber) { previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); previous_fiber = nullptr; } entry_point(start_parameter); } 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(); 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->guard.lock(); fiber->is_thread_fiber = true; return fiber; } #endif } // namespace Common