Util: Add fcontext fiber implementation

This commit is contained in:
SSimco 2026-05-09 16:34:40 +03:00
parent c2e807f8a3
commit 54f043c0f2
10 changed files with 159 additions and 44 deletions

View File

@ -154,6 +154,9 @@ find_package(CURL REQUIRED)
find_package(pugixml REQUIRED)
find_package(RapidJSON REQUIRED)
find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED)
if(ANDROID)
find_package(Boost COMPONENTS context REQUIRED)
endif()
find_package(libzip REQUIRED)
find_package(glslang REQUIRED)
find_package(ZLIB REQUIRED)

View File

@ -74,7 +74,12 @@ if(WIN32)
target_sources(CemuUtil PRIVATE MemMapper/MemMapperWin.cpp)
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoWin.cpp)
elseif(UNIX)
target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp)
if(ANDROID)
target_sources(CemuUtil PRIVATE Fiber/FiberFContext.cpp)
target_link_libraries(CemuUtil PRIVATE Boost::context)
else()
target_sources(CemuUtil PRIVATE Fiber/FiberUContext.cpp)
endif()
target_sources(CemuUtil PRIVATE MemMapper/MemMapperUnix.cpp)
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoUnix.cpp)
if(NOT APPLE)

View File

@ -1,22 +1,9 @@
#pragma once
#if BOOST_OS_WINDOWS
#if BOOST_PLAT_ANDROID
#include "FiberFContext.h"
#elif BOOST_OS_WINDOWS
#include "FiberWin.h"
#else
#include "FiberUContext.h"
#endif
class Fiber
{
public:
Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData);
~Fiber();
static Fiber* PrepareCurrentThread(void* privateData = nullptr);
static void Switch(Fiber& targetFiber);
static void* GetFiberPrivateData();
private:
Fiber(void* privateData); // fiber from current thread
void* m_implData{nullptr};
void* m_privateData;
void* m_stackPtr{ nullptr };
};

View File

@ -0,0 +1,55 @@
#include "FiberFContext.h"
thread_local Fiber* sCurrentFiber = nullptr;
using namespace boost::context::detail;
Fiber::Fiber(void (*fiberEntryPoint)(void* userParam), void* userParam, void* privateData)
: m_entryPoint(fiberEntryPoint),
m_userParam(userParam),
m_privateData(privateData)
{
const size_t stackSize = 2 * 1024 * 1024;
m_stackPtr = malloc(stackSize);
m_context = make_fcontext(static_cast<uint8*>(m_stackPtr) + stackSize, stackSize, Fiber::Start);
}
Fiber::Fiber(void* privateData) : m_privateData(privateData)
{
}
void Fiber::Start(transfer_t transfer)
{
auto fiber = static_cast<Fiber*>(transfer.data);
fiber->m_prevFiber->m_context = transfer.fctx;
fiber->m_entryPoint(fiber->m_userParam);
}
Fiber::~Fiber()
{
if (m_stackPtr)
free(m_stackPtr);
}
Fiber* Fiber::PrepareCurrentThread(void* privateData)
{
sCurrentFiber = new Fiber(privateData);
return sCurrentFiber;
}
void Fiber::Switch(Fiber& targetFiber)
{
if (&targetFiber == sCurrentFiber)
return;
Fiber* thisFiber = sCurrentFiber;
sCurrentFiber = &targetFiber;
targetFiber.m_prevFiber = thisFiber;
transfer_t transfer = jump_fcontext(targetFiber.m_context, &targetFiber);
thisFiber->m_prevFiber->m_context = transfer.fctx;
}
void* Fiber::GetFiberPrivateData()
{
return sCurrentFiber->m_privateData;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <boost/context/detail/fcontext.hpp>
class Fiber
{
public:
Fiber(void (*FiberEntryPoint)(void* userParam), void* userParam, void* privateData);
~Fiber();
static Fiber* PrepareCurrentThread(void* privateData = nullptr);
static void Switch(Fiber& targetFiber);
static void* GetFiberPrivateData();
private:
static void Start(boost::context::detail::transfer_t transfer);
Fiber(void* privateData); // fiber from current thread
boost::context::detail::fcontext_t m_context{};
void (*m_entryPoint)(void* userParam){};
void* m_userParam{};
void* m_privateData;
void* m_stackPtr{};
Fiber* m_prevFiber{};
};

View File

@ -1,34 +1,30 @@
#include "Fiber.h"
#include <ucontext.h>
#include <atomic>
#include "FiberUContext.h"
thread_local Fiber* sCurrentFiber{};
Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData) : m_privateData(privateData)
{
ucontext_t* ctx = (ucontext_t*)malloc(sizeof(ucontext_t));
m_ctx = (ucontext_t*)malloc(sizeof(ucontext_t));
const size_t stackSize = 2 * 1024 * 1024;
m_stackPtr = malloc(stackSize);
getcontext(ctx);
ctx->uc_stack.ss_sp = m_stackPtr;
ctx->uc_stack.ss_size = stackSize;
ctx->uc_link = &ctx[0];
getcontext(m_ctx);
m_ctx->uc_stack.ss_sp = m_stackPtr;
m_ctx->uc_stack.ss_size = stackSize;
m_ctx->uc_link = &m_ctx[0];
#ifdef __arm64__
// https://www.man7.org/linux/man-pages/man3/makecontext.3.html#NOTES
makecontext(ctx, (void(*)())FiberEntryPoint, 2, (uint64) userParam >> 32, userParam);
makecontext(m_ctx, (void(*)())FiberEntryPoint, 2, (uint64) userParam >> 32, userParam);
#else
makecontext(ctx, (void(*)())FiberEntryPoint, 1, userParam);
makecontext(m_ctx, (void(*)())FiberEntryPoint, 1, userParam);
#endif
this->m_implData = (void*)ctx;
}
Fiber::Fiber(void* privateData) : m_privateData(privateData)
{
ucontext_t* ctx = (ucontext_t*)malloc(sizeof(ucontext_t));
getcontext(ctx);
this->m_implData = (void*)ctx;
m_ctx = (ucontext_t*)malloc(sizeof(ucontext_t));
getcontext(m_ctx);
m_stackPtr = nullptr;
}
@ -36,7 +32,7 @@ Fiber::~Fiber()
{
if(m_stackPtr)
free(m_stackPtr);
free(m_implData);
free(m_ctx);
}
Fiber* Fiber::PrepareCurrentThread(void* privateData)
@ -51,7 +47,7 @@ void Fiber::Switch(Fiber& targetFiber)
Fiber* leavingFiber = sCurrentFiber;
sCurrentFiber = &targetFiber;
std::atomic_thread_fence(std::memory_order_seq_cst);
swapcontext((ucontext_t*)(leavingFiber->m_implData), (ucontext_t*)(targetFiber.m_implData));
swapcontext(leavingFiber->m_ctx, targetFiber.m_ctx);
std::atomic_thread_fence(std::memory_order_seq_cst);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <ucontext.h>
class Fiber
{
public:
Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData);
~Fiber();
static Fiber* PrepareCurrentThread(void* privateData = nullptr);
static void Switch(Fiber& targetFiber);
static void* GetFiberPrivateData();
private:
Fiber(void* privateData); // fiber from current thread
ucontext_t* m_ctx{nullptr};
void* m_privateData;
void* m_stackPtr{ nullptr };
};

View File

@ -1,23 +1,20 @@
#include "Fiber.h"
#include <Windows.h>
#include "FiberWin.h"
thread_local Fiber* sCurrentFiber{};
Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData) : m_privateData(privateData)
{
HANDLE fiberHandle = CreateFiber(2 * 1024 * 1024, (LPFIBER_START_ROUTINE)FiberEntryPoint, userParam);
this->m_implData = (void*)fiberHandle;
m_handle = CreateFiber(2 * 1024 * 1024, (LPFIBER_START_ROUTINE)FiberEntryPoint, userParam);
}
Fiber::Fiber(void* privateData) : m_privateData(privateData)
{
this->m_implData = (void*)ConvertThreadToFiber(nullptr);
this->m_stackPtr = nullptr;
m_handle = ConvertThreadToFiber(nullptr);
}
Fiber::~Fiber()
{
DeleteFiber((HANDLE)m_implData);
DeleteFiber(m_handle);
}
Fiber* Fiber::PrepareCurrentThread(void* privateData)
@ -31,7 +28,7 @@ Fiber* Fiber::PrepareCurrentThread(void* privateData)
void Fiber::Switch(Fiber& targetFiber)
{
sCurrentFiber = &targetFiber;
SwitchToFiber((HANDLE)targetFiber.m_implData);
SwitchToFiber(targetFiber.m_handle);
}
void* Fiber::GetFiberPrivateData()

19
src/util/Fiber/FiberWin.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <Windows.h>
class Fiber
{
public:
Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData);
~Fiber();
static Fiber* PrepareCurrentThread(void* privateData = nullptr);
static void Switch(Fiber& targetFiber);
static void* GetFiberPrivateData();
private:
Fiber(void* privateData); // fiber from current thread
HANDLE m_handle{nullptr};
void* m_privateData;
};

View File

@ -27,6 +27,10 @@
"boost-ptr-container",
"boost-property-tree",
"boost-static-string",
{
"name": "boost-context",
"platform": "android"
},
"boost-random",
{
"name": "fmt",