Files
aee/source/core/system/fiber.cpp

142 lines
3.2 KiB
C++

#include "core/system/fiber.hpp"
#include <cstdlib>
#include <cstring>
#include <iostream>
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
// ucontext_t està marcat com a obsolet a POSIX.1-2008 però continua
// funcional a glibc Linux i macOS. Si en el futur migrem a una alternativa
// (boost::context, makecontext personalitzat) només cal tocar aquest fitxer.
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <ucontext.h>
#endif
namespace GameFiber {
namespace {
bool initialized_ = false;
bool done_ = false;
EntryFn entry_fn_ = nullptr;
#if defined(_WIN32)
LPVOID main_fiber_ = nullptr;
LPVOID game_fiber_ = nullptr;
void __stdcall trampoline(void* /*param*/) {
if (entry_fn_) entry_fn_();
done_ = true;
SwitchToFiber(main_fiber_);
}
#else
ucontext_t main_ctx_{};
ucontext_t fiber_ctx_{};
void* fiber_stack_ = nullptr;
void trampoline() {
if (entry_fn_) entry_fn_();
done_ = true;
// Retornar al main: uc_link apunta a main_ctx_ en init().
}
#endif
} // namespace
void init(EntryFn entry, std::size_t stack_size) {
if (initialized_) destroy();
entry_fn_ = entry;
done_ = false;
#if defined(_WIN32)
main_fiber_ = ConvertThreadToFiber(nullptr);
if (!main_fiber_) {
// Ja era un fiber (no sol passar en el main thread d'una app SDL).
main_fiber_ = GetCurrentFiber();
}
game_fiber_ = CreateFiber(stack_size, trampoline, nullptr);
if (!game_fiber_) {
std::cerr << "GameFiber::init: CreateFiber failed\n";
return;
}
#else
fiber_stack_ = std::malloc(stack_size);
if (!fiber_stack_) {
std::cerr << "GameFiber::init: malloc failed\n";
return;
}
getcontext(&fiber_ctx_);
fiber_ctx_.uc_stack.ss_sp = fiber_stack_;
fiber_ctx_.uc_stack.ss_size = stack_size;
fiber_ctx_.uc_link = &main_ctx_;
makecontext(&fiber_ctx_, trampoline, 0);
#endif
initialized_ = true;
}
void destroy() {
if (!initialized_) return;
#if defined(_WIN32)
if (game_fiber_) {
DeleteFiber(game_fiber_);
game_fiber_ = nullptr;
}
// No desconvertim el main thread: SDL pot estar-ne pendent i ja no
// tornem a crear fibers en aquesta execució. ConvertFiberToThread()
// només cal si volguerem reutilitzar el main com a thread normal.
#else
if (fiber_stack_) {
std::free(fiber_stack_);
fiber_stack_ = nullptr;
}
#endif
initialized_ = false;
done_ = false;
entry_fn_ = nullptr;
}
void resume() {
if (!initialized_ || done_) return;
#if defined(_WIN32)
SwitchToFiber(game_fiber_);
#else
swapcontext(&main_ctx_, &fiber_ctx_);
#endif
}
void yield() {
if (!initialized_) return;
#if defined(_WIN32)
SwitchToFiber(main_fiber_);
#else
swapcontext(&fiber_ctx_, &main_ctx_);
#endif
}
bool is_done() { return done_; }
bool is_initialized() { return initialized_; }
} // namespace GameFiber
#if !defined(_WIN32)
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#endif