Removed custom fibers implementation due to issues caused on Windows

Looks like the Windows ATL doesn't play along well at all. What a bummer.
This commit is contained in:
spectranator 2024-05-08 07:58:20 +02:00
parent 1df1841ad1
commit 036a7edb2c
9 changed files with 153 additions and 2159 deletions

3
.gitmodules vendored
View file

@ -55,9 +55,6 @@
[submodule "externals/SPIRV-Headers"]
path = externals/SPIRV-Headers
url = https://github.com/KhronosGroup/SPIRV-Headers.git
[submodule "externals/boost-headers"]
path = externals/boost-headers
url = https://github.com/boostorg/headers.git
[submodule "externals/SPIRV-Tools"]
path = externals/SPIRV-Tools
url = https://github.com/KhronosGroup/SPIRV-Tools.git

View file

@ -275,8 +275,17 @@ endif()
# Configure C++ standard
# ===========================
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# boost asio's concept usage doesn't play nicely with some compilers yet.
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++20>)
# boost still makes use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
else()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
# Output binaries to bin/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
@ -285,6 +294,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
# =======================================================================
# Enforce the search mode of non-required packages for better and shorter failure messages
find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
@ -674,6 +684,8 @@ function(create_target_directory_groups target_name)
endforeach()
endfunction()
# Prevent boost from linking against libs when building
target_link_libraries(Boost::headers INTERFACE Boost::disable_autolinking)
# Adjustments for MSVC + Ninja
if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja")
add_compile_options(

View file

@ -72,7 +72,7 @@ Alternatively, you can clone from the [GitHub mirror repository](https://github.
Note that above repository may be taken down any time. Do not rely on its existence in production. In case the GitHub mirror goes down, a mirror will be set up on Gitlab.
This project incorporates several commits from the [Suyu](https://suyu.dev) and [Sudachi](https://github.com/sudachi-emu/sudachi) forks (but cleaned up due to the typically mediocre code/commit quality from both projects) as well as some custom changes, including lowered minimum boost library version requirements and faster fibers.
This project incorporates several commits from the [Suyu](https://suyu.dev) and [Sudachi](https://github.com/sudachi-emu/sudachi) forks (but cleaned up due to the typically mediocre code/commit quality from both projects) as well as changes listed in **Changes**.
## Building

View file

@ -172,9 +172,6 @@ if (YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS)
add_subdirectory(SPIRV-Tools)
endif()
# Boost headers
add_subdirectory(boost-headers)
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)

View file

@ -259,7 +259,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
)
endif()
target_link_libraries(common PUBLIC Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (ANDROID)

View file

@ -5,46 +5,87 @@
#include "common/assert.h"
#include "common/fiber.h"
#define MINICORO_IMPL
#include "common/minicoro.h"
#include "common/virtual_buffer.h"
#include <boost/context/detail/fcontext.hpp>
namespace Common {
constexpr std::size_t default_stack_size = 512 * 1024;
struct Fiber::FiberImpl {
FiberImpl() {}
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
VirtualBuffer<u8> stack;
VirtualBuffer<u8> rewind_stack;
std::mutex guard;
bool released{};
bool is_thread_fiber{};
Fiber* next_fiber{};
Fiber** next_fiber_ptr;
std::function<void()> entry_point;
std::function<void()> rewind_point;
std::shared_ptr<Fiber> previous_fiber;
bool is_thread_fiber{};
bool released{};
mco_coro* context;
u8* stack_limit{};
u8* rewind_stack_limit{};
boost::context::detail::fcontext_t context{};
boost::context::detail::fcontext_t rewind_context{};
};
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {
impl->is_thread_fiber = true;
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
impl->rewind_point = std::move(rewind_func);
}
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->context = transfer.fctx;
impl->previous_fiber->impl->guard.unlock();
impl->previous_fiber.reset();
impl->entry_point();
UNREACHABLE();
}
void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& 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;
impl->rewind_point();
UNREACHABLE();
}
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->Start(transfer);
}
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->OnRewind(transfer);
}
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
auto desc = mco_desc_init(
[](mco_coro* coro) { reinterpret_cast<Fiber*>(coro->user_data)->impl->entry_point(); }, 0);
desc.user_data = this;
mco_result res = mco_create(&impl->context, &desc);
ASSERT(res == MCO_SUCCESS);
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);
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (impl->released) {
return;
}
DestroyPre();
if (impl->is_thread_fiber) {
DestroyThreadFiber();
} else {
DestroyWorkFiber();
// Make sure the Fiber is not being used
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
impl->guard.unlock();
}
}
@ -53,66 +94,42 @@ void Fiber::Exit() {
if (!impl->is_thread_fiber) {
return;
}
DestroyPre();
DestroyThreadFiber();
}
void Fiber::DestroyPre() {
// Make sure the Fiber is not being used
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
impl->guard.unlock();
}
impl->guard.unlock();
impl->released = true;
}
void Fiber::DestroyWorkFiber() {
mco_result res = mco_destroy(impl->context);
ASSERT(res == MCO_SUCCESS);
}
void Fiber::DestroyThreadFiber() {
if (impl->next_fiber_ptr && *impl->next_fiber_ptr) {
*impl->next_fiber_ptr = nullptr;
}
void Fiber::Rewind() {
ASSERT(impl->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::weak_ptr<Fiber> weak_from, Fiber& to) {
to.impl->guard.lock();
to.impl->previous_fiber = weak_from.lock();
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
// "from" might no longer be valid if the thread was killed
if (auto from = weak_from.lock()) {
if (!from->impl->is_thread_fiber) {
// Set next fiber
from->impl->next_fiber = &to;
// Yield from thread
if (!from->impl->released) {
from->impl->guard.unlock();
mco_yield(from->impl->context);
}
} else {
from->impl->guard.lock();
// Keep running next fiber until they've ran out
auto& next_fiber_ptr = from->impl->next_fiber_ptr;
next_fiber_ptr = &from->impl->next_fiber;
*next_fiber_ptr = &to;
while (*next_fiber_ptr) {
auto next = *next_fiber_ptr;
*next_fiber_ptr = nullptr;
next_fiber_ptr = &next->impl->next_fiber;
// Stop if new thread is thread fiber
if (next->impl->is_thread_fiber)
break;
// Resume new thread
next->impl->guard.lock();
mco_result res = mco_resume(next->impl->context);
ASSERT(res == MCO_SUCCESS);
}
from->impl->guard.unlock();
if (from->impl->previous_fiber == nullptr) {
ASSERT_MSG(false, "previous_fiber is nullptr!");
return;
}
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
return std::shared_ptr<Fiber>{new Fiber()};
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->impl->guard.lock();
fiber->impl->is_thread_fiber = true;
return fiber;
}
} // namespace Common

View file

@ -6,7 +6,9 @@
#include <functional>
#include <memory>
#include "common/minicoro.h"
namespace boost::context::detail {
struct transfer_t;
}
namespace Common {
@ -36,18 +38,25 @@ public:
Fiber(Fiber&&) = default;
Fiber& operator=(Fiber&&) = default;
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void()>&& rewind_func);
void Rewind();
/// Only call from main thread's fiber
void Exit();
private:
Fiber();
void DestroyPre();
void DestroyWorkFiber();
void DestroyThreadFiber();
void OnRewind(boost::context::detail::transfer_t& transfer);
void Start(boost::context::detail::transfer_t& transfer);
static void FiberStartFunc(boost::context::detail::transfer_t transfer);
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
struct FiberImpl;
std::unique_ptr<FiberImpl> impl;

File diff suppressed because it is too large Load diff

View file

@ -271,4 +271,43 @@ TEST_CASE("Fibers::StartRace", "[common]") {
REQUIRE(test_control.value3 == 1);
}
class TestControl4;
class TestControl4 {
public:
TestControl4() {
fiber1 = std::make_shared<Fiber>([this] { DoWork(); });
goal_reached = false;
rewinded = false;
}
void Execute() {
thread_fiber = Fiber::ThreadToFiber();
Fiber::YieldTo(thread_fiber, *fiber1);
thread_fiber->Exit();
}
void DoWork() {
fiber1->SetRewindPoint([this] { DoWork(); });
if (rewinded) {
goal_reached = true;
Fiber::YieldTo(fiber1, *thread_fiber);
}
rewinded = true;
fiber1->Rewind();
}
std::shared_ptr<Common::Fiber> fiber1;
std::shared_ptr<Common::Fiber> thread_fiber;
bool goal_reached;
bool rewinded;
};
TEST_CASE("Fibers::Rewind", "[common]") {
TestControl4 test_control{};
test_control.Execute();
REQUIRE(test_control.goal_reached);
REQUIRE(test_control.rewinded);
}
} // namespace Common