mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 09:11:52 -04:00
139 lines
5.2 KiB
C++
139 lines
5.2 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
|
|
#if defined(_WIN32)
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "ppc_runtime.h"
|
|
|
|
// Forward declarations
|
|
struct CpuContext;
|
|
|
|
// GuestFiberManager: each guest OSThread maps to a Windows Fiber. A scheduler fiber picks
|
|
// which guest fiber runs; a real timer thread queues VI retraces at the VI cadence. Guest
|
|
// threads only switch at explicit yield points (OSSleepThread, OSYieldThread, ...), matching
|
|
// Wii cooperative semantics exactly.
|
|
|
|
namespace Fiber {
|
|
|
|
// Thread states matching guest OS
|
|
enum class ThreadState : uint32_t {
|
|
READY = 1,
|
|
RUNNING = 2,
|
|
WAITING = 4,
|
|
MORIBUND = 8,
|
|
};
|
|
|
|
// Information about a guest fiber
|
|
struct GuestFiber {
|
|
void* fiber = nullptr; // Windows fiber handle
|
|
uint32_t entryPoint = 0; // Thread entry function
|
|
uint32_t entryArg = 0; // Argument to entry function
|
|
CpuContext cpuContext{}; // Saved CPU context for this fiber
|
|
ThreadState state = ThreadState::READY;
|
|
bool isSchedulerFiber = false; // True for the main scheduler fiber
|
|
bool terminated = false; // Thread has exited
|
|
};
|
|
|
|
class GuestFiberManager {
|
|
public:
|
|
// Initialize the fiber system - must be called from main thread
|
|
static void Initialize();
|
|
|
|
// Shutdown the fiber system
|
|
static void Shutdown();
|
|
|
|
// Check if fiber system is initialized
|
|
static bool IsInitialized();
|
|
|
|
// Create a fiber for a guest thread (called from OSCreateThread HLE).
|
|
// OSCreateThread's stackSize/priority are not passed: the host fiber models
|
|
// neither, only the guest SP seeded from stackBase. Returns true on success.
|
|
static bool CreateGuestFiber(uint32_t guestThreadAddr, uint32_t entryPoint,
|
|
uint32_t entryArg, uint32_t stackBase);
|
|
|
|
// Resume a guest thread (called from OSResumeThread HLE)
|
|
// This marks the fiber as runnable
|
|
static void ResumeGuestThread(uint32_t guestThreadAddr);
|
|
|
|
// Suspend a guest thread (called from OSSuspendThread HLE)
|
|
static void SuspendGuestThread(uint32_t guestThreadAddr);
|
|
|
|
// Called when a guest thread exits or is canceled
|
|
static void ExitGuestThread(uint32_t guestThreadAddr, ThreadState finalState);
|
|
|
|
// Switch to a specific guest thread's fiber (called from OSLoadContext)
|
|
// This saves the current fiber's state and switches to the target
|
|
static void SwitchToThread(uint32_t guestThreadAddr, CpuContext* cpu);
|
|
|
|
// Get the currently running guest thread address (0 if in scheduler)
|
|
static uint32_t GetCurrentGuestThread();
|
|
|
|
// Get fiber info for a guest thread (may be null)
|
|
static GuestFiber* GetFiber(uint32_t guestThreadAddr);
|
|
|
|
// Check for VI retrace and process it (called from scheduler idle)
|
|
static void ProcessTimerEvents(CpuContext* cpu);
|
|
|
|
// Register the current host fiber as a guest thread fiber
|
|
// This is used for the default/main thread that exists before OSCreateThread
|
|
static bool RegisterMainThreadAsFiber(uint32_t guestThreadAddr, CpuContext* cpu);
|
|
|
|
// Check if a fiber exists for a guest thread
|
|
static bool HasFiber(uint32_t guestThreadAddr);
|
|
static bool IsTerminated(uint32_t guestThreadAddr);
|
|
|
|
private:
|
|
// The fiber entry point wrapper
|
|
#if defined(_WIN32)
|
|
static void CALLBACK FiberProc(void* param);
|
|
#else
|
|
static void FiberProc(void* param);
|
|
// libco's co_create() entry points take no argument (unlike CreateFiber's FiberProc(void*)),
|
|
// so this trampoline reads the guest thread address staged by CreateGuestFiber() and forwards
|
|
// into the (platform-neutral-bodied) FiberProc above. See fiber_manager.cpp.
|
|
static void FiberProcTrampoline();
|
|
#endif
|
|
// Switch from whichever fiber is currently active straight to the scheduler fiber, without
|
|
// the SwitchToThread bookkeeping (CPU context save/restore, s_currentGuestThread). Used for
|
|
// in-fiber yields that aren't a real guest thread switch: waiting out the EGG::Thread::start
|
|
// deferral loop, and returning control on natural thread exit.
|
|
static void SwitchToScheduler();
|
|
|
|
// Internal state
|
|
static std::mutex s_mutex;
|
|
static std::unordered_map<uint32_t, GuestFiber> s_fibers;
|
|
static std::vector<void*> s_fibersPendingDelete;
|
|
// The scheduler's own "fiber": a Windows HFIBER, or (non-Windows) libco's cothread_t for
|
|
// whichever native call stack first called GuestFiberManager::Initialize() - both are
|
|
// plain void* handles, so one field serves both platforms.
|
|
static void* s_schedulerFiber;
|
|
static uint32_t s_currentGuestThread;
|
|
static bool s_initialized;
|
|
// Stored CPU context pointer for fiber switches
|
|
static thread_local CpuContext* s_cpuContext;
|
|
|
|
static void PurgePendingFibers();
|
|
};
|
|
|
|
// Global pending retrace count. GuestFiberManager::ProcessTimerEvents drains
|
|
// this on the guest thread so VI callbacks still run with guest state/locking
|
|
// expectations.
|
|
extern std::atomic<uint32_t> g_viRetracePendingCount;
|
|
|
|
} // namespace Fiber
|
|
|