// OSMutex.cpp - PC implementation of GameCube OSMutex/OSCond API // Uses std::recursive_mutex and std::condition_variable_any behind the // unchanged GameCube C API. The OSMutex struct layout is preserved so // game code can read its fields. #include #include #include #include #include #include #include #include "JSystem/JKernel/JKRHeap.h" // ============================================================================ // Side-table: native mutex per OSMutex // ============================================================================ struct PCMutexData { std::recursive_mutex nativeMutex; }; // Lazy-initialized to avoid DLL static init crashes static std::mutex& GetMutexMapMutex() { static std::mutex mtx; return mtx; } static std::unordered_map>& GetMutexMap() { static std::unordered_map> map; return map; } static PCMutexData& GetMutexData(OSMutex* mutex) { std::lock_guard lock(GetMutexMapMutex()); auto& map = GetMutexMap(); auto it = map.find(mutex); if (it == map.end()) { auto result = map.emplace(mutex, std::make_unique()); return *result.first->second; } return *it->second; } // ============================================================================ // Side-table: native condition variable per OSCond // ============================================================================ struct PCCondData { std::condition_variable_any cv; }; // Lazy-initialized to avoid DLL static init crashes static std::mutex& GetCondMapMutex() { static std::mutex mtx; return mtx; } static std::unordered_map>& GetCondMap() { static std::unordered_map> map; return map; } static PCCondData& GetCondData(OSCond* cond) { std::lock_guard lock(GetCondMapMutex()); auto& map = GetCondMap(); auto it = map.find(cond); if (it == map.end()) { auto result = map.emplace(cond, std::make_unique()); return *result.first->second; } return *it->second; } void ClearCondMap() { std::lock_guard lock(GetCondMapMutex()); auto& map = GetCondMap(); for (auto& pair : map) { pair.second->cv.notify_all(); } map.clear(); } // ============================================================================ // C API functions // ============================================================================ extern "C" { void OSInitMutex(OSMutex* mutex) { if (!mutex) return; OSInitThreadQueue(&mutex->queue); mutex->thread = nullptr; mutex->count = 0; // Create/reset side-table entry GetMutexData(mutex); } void OSLockMutex(OSMutex* mutex) { if (!mutex) return; PCMutexData& data = GetMutexData(mutex); data.nativeMutex.lock(); // Update GC-visible fields OSThread* currentThread = OSGetCurrentThread(); mutex->thread = currentThread; mutex->count++; } void OSUnlockMutex(OSMutex* mutex) { if (!mutex) return; OSThread* currentThread = OSGetCurrentThread(); if (mutex->thread != currentThread) return; mutex->count--; if (mutex->count == 0) { mutex->thread = nullptr; } PCMutexData& data = GetMutexData(mutex); data.nativeMutex.unlock(); } BOOL OSTryLockMutex(OSMutex* mutex) { if (!mutex) return FALSE; PCMutexData& data = GetMutexData(mutex); if (data.nativeMutex.try_lock()) { OSThread* currentThread = OSGetCurrentThread(); mutex->thread = currentThread; mutex->count++; return TRUE; } return FALSE; } // ============================================================================ // Internal: unlock all mutexes held by a thread (called on thread exit) // ============================================================================ void __OSUnlockAllMutex(OSThread* thread) { // On GC this walks the thread's mutex queue. // On PC the native mutexes are cleaned up when threads exit. // Clear the GC-visible queue. if (!thread) return; thread->queueMutex.head = nullptr; thread->queueMutex.tail = nullptr; } int __OSCheckDeadLock(OSThread* thread) { // Simplified: native OS handles deadlock detection. return 0; } int __OSCheckMutexes(OSThread* thread) { return 1; } // ============================================================================ // Condition Variable API // ============================================================================ void OSInitCond(OSCond* cond) { if (!cond) return; OSInitThreadQueue(&cond->queue); GetCondData(cond); } void OSWaitCond(OSCond* cond, OSMutex* mutex) { if (!cond || !mutex) return; PCCondData& condData = GetCondData(cond); PCMutexData& mutexData = GetMutexData(mutex); // Save and clear the GC mutex state OSThread* currentThread = OSGetCurrentThread(); s32 savedCount = mutex->count; mutex->count = 0; mutex->thread = nullptr; // Unlock the recursive mutex the same number of times it was locked for (s32 i = 0; i < savedCount; i++) { mutexData.nativeMutex.unlock(); } // Wait on the condition variable { std::unique_lock lock(mutexData.nativeMutex); condData.cv.wait(lock); } // Re-lock the recursive mutex the same number of times for (s32 i = 0; i < savedCount; i++) { mutexData.nativeMutex.lock(); } // Restore GC mutex state mutex->thread = currentThread; mutex->count = savedCount; } void OSSignalCond(OSCond* cond) { if (!cond) return; PCCondData& condData = GetCondData(cond); condData.cv.notify_all(); } #ifdef __cplusplus } #endif