142 lines
3.2 KiB
C++
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
|