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"] [submodule "externals/SPIRV-Headers"]
path = externals/SPIRV-Headers path = externals/SPIRV-Headers
url = https://github.com/KhronosGroup/SPIRV-Headers.git 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"] [submodule "externals/SPIRV-Tools"]
path = externals/SPIRV-Tools path = externals/SPIRV-Tools
url = https://github.com/KhronosGroup/SPIRV-Tools.git url = https://github.com/KhronosGroup/SPIRV-Tools.git

View file

@ -275,8 +275,17 @@ endif()
# Configure C++ standard # Configure C++ standard
# =========================== # ===========================
# 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 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
# Output binaries to bin/ # Output binaries to bin/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/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 # 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(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED) find_package(fmt 9 REQUIRED)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
@ -674,6 +684,8 @@ function(create_target_directory_groups target_name)
endforeach() endforeach()
endfunction() endfunction()
# Prevent boost from linking against libs when building
target_link_libraries(Boost::headers INTERFACE Boost::disable_autolinking)
# Adjustments for MSVC + Ninja # Adjustments for MSVC + Ninja
if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja") if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja")
add_compile_options( 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. 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 ## Building

View file

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

View file

@ -259,7 +259,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
) )
endif() 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) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (ANDROID) if (ANDROID)

View file

@ -5,46 +5,87 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/fiber.h" #include "common/fiber.h"
#define MINICORO_IMPL #include "common/virtual_buffer.h"
#include "common/minicoro.h"
#include <boost/context/detail/fcontext.hpp>
namespace Common { namespace Common {
constexpr std::size_t default_stack_size = 512 * 1024;
struct Fiber::FiberImpl { struct Fiber::FiberImpl {
FiberImpl() {} FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
VirtualBuffer<u8> stack;
VirtualBuffer<u8> rewind_stack;
std::mutex guard; std::mutex guard;
bool released{};
bool is_thread_fiber{};
Fiber* next_fiber{};
Fiber** next_fiber_ptr;
std::function<void()> entry_point; 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>()} { void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
impl->is_thread_fiber = true; 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>()} { Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func); impl->entry_point = std::move(entry_point_func);
auto desc = mco_desc_init( impl->stack_limit = impl->stack.data();
[](mco_coro* coro) { reinterpret_cast<Fiber*>(coro->user_data)->impl->entry_point(); }, 0); impl->rewind_stack_limit = impl->rewind_stack.data();
desc.user_data = this; u8* stack_base = impl->stack_limit + default_stack_size;
mco_result res = mco_create(&impl->context, &desc); impl->context =
ASSERT(res == MCO_SUCCESS); boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
} }
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() { Fiber::~Fiber() {
if (impl->released) { if (impl->released) {
return; return;
} }
DestroyPre(); // Make sure the Fiber is not being used
if (impl->is_thread_fiber) { const bool locked = impl->guard.try_lock();
DestroyThreadFiber(); ASSERT_MSG(locked, "Destroying a fiber that's still running");
} else { if (locked) {
DestroyWorkFiber(); impl->guard.unlock();
} }
} }
@ -53,66 +94,42 @@ void Fiber::Exit() {
if (!impl->is_thread_fiber) { if (!impl->is_thread_fiber) {
return; 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; impl->released = true;
} }
void Fiber::DestroyWorkFiber() { void Fiber::Rewind() {
mco_result res = mco_destroy(impl->context); ASSERT(impl->rewind_point);
ASSERT(res == MCO_SUCCESS); ASSERT(impl->rewind_context == nullptr);
} u8* stack_base = impl->rewind_stack_limit + default_stack_size;
impl->rewind_context =
void Fiber::DestroyThreadFiber() { boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
if (impl->next_fiber_ptr && *impl->next_fiber_ptr) { boost::context::detail::jump_fcontext(impl->rewind_context, this);
*impl->next_fiber_ptr = nullptr;
}
} }
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) { 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 (auto from = weak_from.lock()) {
if (!from->impl->is_thread_fiber) { if (from->impl->previous_fiber == nullptr) {
// Set next fiber ASSERT_MSG(false, "previous_fiber is nullptr!");
from->impl->next_fiber = &to; return;
// 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();
} }
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() { 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 } // namespace Common

View file

@ -6,7 +6,9 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "common/minicoro.h" namespace boost::context::detail {
struct transfer_t;
}
namespace Common { namespace Common {
@ -36,18 +38,25 @@ public:
Fiber(Fiber&&) = default; Fiber(Fiber&&) = default;
Fiber& operator=(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); 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()>&& rewind_func);
void Rewind();
/// Only call from main thread's fiber /// Only call from main thread's fiber
void Exit(); void Exit();
private: private:
Fiber(); Fiber();
void DestroyPre(); void OnRewind(boost::context::detail::transfer_t& transfer);
void DestroyWorkFiber(); void Start(boost::context::detail::transfer_t& transfer);
void DestroyThreadFiber(); static void FiberStartFunc(boost::context::detail::transfer_t transfer);
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
struct FiberImpl; struct FiberImpl;
std::unique_ptr<FiberImpl> impl; 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); 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 } // namespace Common