#include "core/system/fiber.hpp" #include #include #include #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #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 #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