/** * Sleep the current thread until the alarm has been triggered or cancelled. * * \return Returns TRUE if alarm expired, FALSE if alarm cancelled */ BOOL OSWaitAlarm(OSAlarm *alarm) { internal::lockScheduler(); internal::acquireIdLock(sAlarmLock, alarm); decaf_check(alarm); decaf_check(alarm->tag == OSAlarm::Tag); if (alarm->state != OSAlarmState::Set) { internal::releaseIdLock(sAlarmLock, alarm); internal::unlockScheduler(); return FALSE; } OSGetCurrentThread()->alarmCancelled = false; internal::sleepThreadNoLock(&alarm->threadQueue); internal::releaseIdLock(sAlarmLock, alarm); internal::rescheduleSelfNoLock(); auto cancelled = OSGetCurrentThread()->alarmCancelled; internal::unlockScheduler(); if (cancelled) { return FALSE; } else { return TRUE; } }
/** * Sleep the current thread until the condition variable has been signalled. * * The mutex must be locked when entering this function. * Will unlock the mutex and then sleep, reacquiring the mutex when woken. * * Similar to <a href="http://en.cppreference.com/w/cpp/thread/condition_variable/wait">std::condition_variable::wait</a>. */ void OSWaitCond(OSCondition *condition, OSMutex *mutex) { auto thread = OSGetCurrentThread(); internal::lockScheduler(); decaf_check(mutex && mutex->tag == OSMutex::Tag); decaf_check(condition && condition->tag == OSCondition::Tag); decaf_check(mutex->owner == thread); // Force an unlock auto mutexCount = mutex->count; mutex->count = 1; unlockMutexNoLock(mutex); internal::rescheduleOtherCoreNoLock(); // Sleep on the condition internal::sleepThreadNoLock(&condition->queue); internal::rescheduleSelfNoLock(); // Restore lock lockMutexNoLock(mutex); mutex->count = mutexCount; internal::unlockScheduler(); }
static bool spinTryLockWithTimeout(OSSpinLock *spinlock, OSTime duration) { auto thread = OSGetCurrentThread(); auto owner = mem::untranslate(thread); if (spinlock->owner.load(std::memory_order_relaxed) == owner) { ++spinlock->recursion; return true; } uint32_t expected = 0; auto timeout = OSGetSystemTime() + duration; while (!spinlock->owner.compare_exchange_weak(expected, owner, std::memory_order_release, std::memory_order_relaxed)) { if (OSGetSystemTime() >= timeout) { return false; } expected = 0; } increaseSpinLockCount(thread); return true; }
/** * Try to lock a mutex. * * If no one owns the mutex, set current thread as owner. * If the lock is owned by the current thread, increase the recursion count. * If the lock is owned by another thread, do not block, return FALSE. * * \return TRUE if the mutex is locked, FALSE if the mutex is owned by another thread. * * Similar to <a href="http://en.cppreference.com/w/cpp/thread/recursive_mutex/try_lock">std::recursive_mutex::try_lock</a>. */ BOOL OSTryLockMutex(virt_ptr<OSMutex> mutex) { internal::lockScheduler(); auto thread = OSGetCurrentThread(); decaf_check(thread->state == OSThreadState::Running); internal::testThreadCancelNoLock(); if (mutex->owner == thread) { mutex->count++; internal::unlockScheduler(); return TRUE; } else if (mutex->owner) { internal::unlockScheduler(); return FALSE; } // Set thread to owner of mutex mutex->count++; mutex->owner = thread; MutexQueue::append(virt_addrof(thread->mutexQueue), mutex); thread->cancelState |= OSThreadCancelState::DisabledByMutex; internal::unlockScheduler(); return TRUE; }
void OSSleepTicks(Time ticks) { if (1) { OSTestThreadCancel(); gLog->warn("Ignoring unimplemented OSSleepTicks"); return; } auto thread = OSGetCurrentThread(); OSLockScheduler(); // Create the alarm user data auto data = OSAllocFromSystem<SleepAlarmData>(); data->thread = thread; // Create an alarm to trigger wakeup auto alarm = OSAllocFromSystem<OSAlarm>(); OSCreateAlarm(alarm); OSSetAlarmUserData(alarm, data); OSSetAlarm(alarm, ticks, pSleepAlarmHandler); // Sleep thread OSSleepThreadNoLock(nullptr); OSRescheduleNoLock(); OSFreeToSystem(data); OSFreeToSystem(alarm); OSUnlockScheduler(); }
static void lockMutexNoLock(virt_ptr<OSMutex> mutex) { auto thread = OSGetCurrentThread(); decaf_check(thread->state == OSThreadState::Running); while (mutex->owner) { if (mutex->owner == thread) { mutex->count++; return; } // Mark this thread as waiting on the mutex thread->mutex = mutex; // Promote mutex owner priority internal::promoteThreadPriorityNoLock(mutex->owner, thread->priority); // Wait for other owner to unlock internal::sleepThreadNoLock(virt_addrof(mutex->queue)); internal::rescheduleSelfNoLock(); // We are no longer waiting on the mutex thread->mutex = nullptr; } // Set current thread to owner of mutex mutex->count++; mutex->owner = thread; MutexQueue::append(virt_addrof(thread->mutexQueue), mutex); thread->cancelState |= OSThreadCancelState::DisabledByMutex; }
static BOOL spinTryLock(OSSpinLock *spinlock) { uint32_t owner, expected; auto thread = OSGetCurrentThread(); if (!thread) { return FALSE; } owner = memory_untranslate(thread); if (spinlock->owner.load(std::memory_order_relaxed) == owner) { ++spinlock->recursion; return TRUE; } expected = 0; if (spinlock->owner.compare_exchange_weak(expected, owner, std::memory_order_release, std::memory_order_relaxed)) { return TRUE; } else { return FALSE; } }
void OSCancelThread(OSThread *thread) { bool reschedule = false; OSLockScheduler(); if (thread->requestFlag == OSThreadRequest::Suspend) { OSWakeupThreadWaitForSuspensionNoLock(&thread->suspendQueue, -1); reschedule = true; } if (thread->suspendCounter != 0) { if (thread->cancelState == 0) { OSResumeThreadNoLock(thread, thread->suspendCounter); reschedule = true; } } if (reschedule) { OSRescheduleNoLock(); } thread->suspendCounter = 0; thread->needSuspend = 0; thread->requestFlag = OSThreadRequest::Cancel; OSUnlockScheduler(); if (OSGetCurrentThread() == thread) { OSExitThread(-1); } }
BOOL OSSetThreadCancelState(BOOL state) { auto thread = OSGetCurrentThread(); auto old = thread->cancelState; thread->cancelState = state; return old; }
void sleepThreadNoLock(OSThreadQueue *queue) { auto thread = OSGetCurrentThread(); thread->queue = queue; thread->state = OSThreadState::Waiting; if (queue) { ThreadQueue::insert(queue, thread); } }
static BOOL spinReleaseLock(OSSpinLock *spinlock) { uint32_t owner; auto thread = OSGetCurrentThread(); if (!thread) { return FALSE; } owner = memory_untranslate(OSGetCurrentThread()); if (spinlock->recursion > 0u) { --spinlock->recursion; return TRUE; } else if (spinlock->owner.load(std::memory_order_relaxed) == owner) { spinlock->owner = 0u; return TRUE; } return FALSE; }
/** * Unlocks the mutex. * * Will decrease the recursion count, will only unlock the mutex when the * recursion count reaches 0. * If any other threads are waiting to lock the mutex they will be woken. * * Similar to <a href="http://en.cppreference.com/w/cpp/thread/recursive_mutex/unlock">std::recursive_mutex::unlock</a>. */ void OSUnlockMutex(virt_ptr<OSMutex> mutex) { internal::lockScheduler(); auto thread = OSGetCurrentThread(); decaf_check(thread->state == OSThreadState::Running); // Not the owner, ignore this call. if (mutex->owner != thread) { internal::unlockScheduler(); return; } // Decrement the mutexes lock count mutex->count--; // If we still own the mutex, lets just leave now if (mutex->count > 0) { internal::unlockScheduler(); return; } // Remove mutex from thread's mutex queue MutexQueue::erase(virt_addrof(thread->mutexQueue), mutex); // Clear the mutex owner mutex->owner = nullptr; // If we have a promoted priority, reset it. if (thread->priority < thread->basePriority) { thread->priority = internal::calculateThreadPriorityNoLock(thread); } // Clear the cancelState flag if we don't have any more mutexes locked if (!thread->mutexQueue.head) { thread->cancelState &= ~OSThreadCancelState::DisabledByMutex; } // Wakeup any threads trying to lock this mutex internal::wakeupThreadNoLock(virt_addrof(mutex->queue)); // Check if we are meant to cancel now internal::testThreadCancelNoLock(); // Reschedule everyone internal::rescheduleAllCoreNoLock(); // Unlock our scheduler and continue internal::unlockScheduler(); }
void testThreadCancelNoLock() { auto thread = OSGetCurrentThread(); if (thread->cancelState) { if (thread->requestFlag == OSThreadRequest::Suspend) { suspendThreadNoLock(thread); rescheduleAllCoreNoLock(); } else if (thread->requestFlag == OSThreadRequest::Cancel) { unlockScheduler(); OSExitThread(-1); } } }
void __OSClearThreadStack32(OSThread *thread, uint32_t value) { uint32_t clearStart = 0, clearEnd = 0; if (OSGetCurrentThread() == thread) { clearStart = thread->stackEnd + 4; clearEnd = OSGetStackPointer(); } else { clearStart = thread->stackEnd + 4; clearEnd = thread->fiber->state.gpr[1]; } for (auto addr = clearStart; addr < clearEnd; addr += 4) { *reinterpret_cast<uint32_t*>(gMemory.translate(addr)) = value; } }
BOOL OSSetThreadStackUsage(OSThread *thread) { OSLockScheduler(); if (!thread) { thread = OSGetCurrentThread(); } else if (thread->state == OSThreadState::Running) { OSUnlockScheduler(); return FALSE; } __OSClearThreadStack32(thread, 0xfefefefe); thread->attr |= OSThreadAttributes::StackUsage; OSUnlockScheduler(); return TRUE; }
void OSExitThread(int value) { auto thread = OSGetCurrentThread(); OSLockScheduler(); thread->exitValue = value; if (thread->attr & OSThreadAttributes::Detached) { thread->state = OSThreadState::None; } else { thread->state = OSThreadState::Moribund; } OSWakeupThreadNoLock(&thread->joinQueue); OSWakeupThreadWaitForSuspensionNoLock(&thread->suspendQueue, -1); OSUnlockScheduler(); gProcessor.exit(); }
static bool spinReleaseLock(OSSpinLock *spinlock) { auto thread = OSGetCurrentThread(); auto owner = mem::untranslate(thread); if (spinlock->recursion > 0u) { --spinlock->recursion; return false; } else if (spinlock->owner.load(std::memory_order_relaxed) == owner) { spinlock->owner = 0u; decreaseSpinLockCount(thread); return true; } decaf_abort("Attempt to release spin lock which is not owned."); return true; }
/** * Wait for an event value to be TRUE with a timeout * * Behaves the same than OSWaitEvent but with a timeout. * * Returns TRUE if the event was signalled, FALSE if wait timed out. */ BOOL OSWaitEventWithTimeout(OSEvent *event, OSTime timeout) { BOOL result = TRUE; OSLockScheduler(); // Check if event is already set if (event->value) { if (event->mode == OSEventMode::AutoReset) { // Reset event event->value = FALSE; } OSUnlockScheduler(); return TRUE; } // Setup some alarm data for callback auto data = OSAllocFromSystem<EventAlarmData>(); data->event = event; data->thread = OSGetCurrentThread(); data->timeout = FALSE; // Create an alarm to trigger timeout auto alarm = OSAllocFromSystem<OSAlarm>(); OSCreateAlarm(alarm); OSSetAlarmUserData(alarm, data); OSSetAlarm(alarm, timeout, pEventAlarmHandler); // Wait for the event OSSleepThreadNoLock(&event->queue); OSRescheduleNoLock(); if (data->timeout) { // Timed out, remove from wait queue OSEraseFromThreadQueue(&event->queue, data->thread); result = FALSE; } OSFreeToSystem(data); OSFreeToSystem(alarm); OSUnlockScheduler(); return result; }
uint32_t OSSuspendThread(OSThread *thread) { OSLockScheduler(); int32_t result; if (thread->state == OSThreadState::Moribund || thread->state == OSThreadState::None) { OSUnlockScheduler(); return -1; } if (thread->requestFlag == OSThreadRequest::Cancel) { OSUnlockScheduler(); return -1; } auto curThread = OSGetCurrentThread(); if (curThread == thread) { if (thread->cancelState) { OSUnlockScheduler(); return -1; } thread->needSuspend++; result = thread->suspendCounter; OSSuspendThreadNoLock(thread); OSRescheduleNoLock(); } else { if (thread->suspendCounter != 0) { result = thread->suspendCounter++; } else { thread->needSuspend++; thread->requestFlag = OSThreadRequest::Suspend; OSSleepThreadNoLock(&thread->suspendQueue); OSRescheduleNoLock(); result = thread->suspendResult; } } OSUnlockScheduler(); return result; }
void OSUnlockMutexNoLock(OSMutex *mutex) { auto thread = OSGetCurrentThread(); assert(mutex && mutex->tag == OSMutex::Tag); assert(mutex->owner == thread); assert(mutex->count > 0); mutex->count--; if (mutex->count == 0) { mutex->owner = nullptr; // Remove mutex from thread's mutex queue OSEraseFromQueue(&thread->mutexQueue, mutex); // Wakeup any threads trying to lock this mutex OSWakeupThreadNoLock(&mutex->queue); OSRescheduleNoLock(); } }
static bool spinTryLock(OSSpinLock *spinlock) { auto thread = OSGetCurrentThread(); auto owner = mem::untranslate(thread); if (spinlock->owner.load(std::memory_order_relaxed) == owner) { ++spinlock->recursion; return true; } uint32_t expected = 0; if (spinlock->owner.compare_exchange_weak(expected, owner, std::memory_order_release, std::memory_order_relaxed)) { increaseSpinLockCount(thread); return true; } else { return false; } }
/** * Sleep the current thread until the condition variable has been signalled. * * The mutex must be locked when entering this function. * Will unlock the mutex and then sleep, reacquiring the mutex when woken. * * Similar to <a href="http://en.cppreference.com/w/cpp/thread/condition_variable/wait">std::condition_variable::wait</a>. */ void OSWaitCond(virt_ptr<OSCondition> condition, virt_ptr<OSMutex> mutex) { internal::lockScheduler(); auto thread = OSGetCurrentThread(); decaf_check(thread->state == OSThreadState::Running); decaf_check(mutex->owner == thread); // Save the count and then unlock the mutex auto mutexCount = mutex->count; mutex->count = 0; mutex->owner = nullptr; // Remove mutex from thread's mutex queue MutexQueue::erase(virt_addrof(thread->mutexQueue), mutex); // If we have a promoted priority, reset it. if (thread->priority < thread->basePriority) { thread->priority = internal::calculateThreadPriorityNoLock(thread); } // Wake anyone waiting on the mutex internal::disableScheduler(); internal::wakeupThreadNoLock(virt_addrof(mutex->queue)); internal::rescheduleAllCoreNoLock(); internal::enableScheduler(); // Sleep on the condition internal::sleepThreadNoLock(virt_addrof(condition->queue)); internal::rescheduleSelfNoLock(); // Relock the mutex lockMutexNoLock(mutex); mutex->count = mutexCount; internal::unlockScheduler(); }
static void lockMutexNoLock(OSMutex *mutex) { // The following check is disabled as not calling InitMutex does // not break behaviour, and some games do not properly initialise // their mutexes before using them. We manually call OSInitMutex // to avoid triggering any of our own assertions later on... // HACK: This might be a hack around some broken behaviour elsewhere // inside of decaf, it's hard to tell... Let's assume not for now. //decaf_check(mutex->tag == OSMutex::Tag); if (mutex->tag != OSMutex::Tag) { OSInitMutex(mutex); } auto thread = OSGetCurrentThread(); while (mutex->owner && mutex->owner != thread) { thread->mutex = mutex; // Promote mutex owner priority internal::promoteThreadPriorityNoLock(mutex->owner, thread->priority); // Wait for other owner to unlock internal::sleepThreadNoLock(&mutex->queue); internal::rescheduleSelfNoLock(); thread->mutex = nullptr; } if (mutex->owner != thread) { // Add to mutex queue MutexQueue::append(&thread->mutexQueue, mutex); } mutex->owner = thread; mutex->count++; }
BOOL OSTryLockMutex(OSMutex *mutex) { auto thread = OSGetCurrentThread(); OSLockScheduler(); OSTestThreadCancelNoLock(); if (mutex->owner && mutex->owner != thread) { // Someone else owns this mutex OSUnlockScheduler(); return FALSE; } if (mutex->owner != thread) { // Add to mutex queue OSAppendQueue(&thread->mutexQueue, mutex); } mutex->owner = thread; mutex->count++; OSUnlockScheduler(); return TRUE; }
void OSLockMutexNoLock(OSMutex *mutex) { assert(mutex && mutex->tag == OSMutex::Tag); auto thread = OSGetCurrentThread(); while (mutex->owner && mutex->owner != thread) { thread->mutex = mutex; // Wait for other owner to unlock OSSleepThreadNoLock(&mutex->queue); OSRescheduleNoLock(); thread->mutex = nullptr; } if (mutex->owner != thread) { // Add to mutex queue OSAppendQueue(&thread->mutexQueue, mutex); } mutex->owner = thread; mutex->count++; }
void OSWaitCond(OSCondition *condition, OSMutex *mutex) { auto thread = OSGetCurrentThread(); OSLockScheduler(); assert(mutex && mutex->tag == OSMutex::Tag); assert(condition && condition->tag == OSCondition::Tag); assert(mutex->owner == thread); // Force an unlock auto mutexCount = mutex->count; mutex->count = 1; OSUnlockMutexNoLock(mutex); // Sleep on the condition OSSleepThreadNoLock(&condition->queue); OSRescheduleNoLock(); // Restore lock OSLockMutexNoLock(mutex); mutex->count = mutexCount; OSUnlockScheduler(); }
/** * Try to lock a mutex. * * If no one owns the mutex, set current thread as owner. * If the lock is owned by the current thread, increase the recursion count. * If the lock is owned by another thread, do not block, return FALSE. * * \return TRUE if the mutex is locked, FALSE if the mutex is owned by another thread. * * Similar to <a href="http://en.cppreference.com/w/cpp/thread/recursive_mutex/try_lock">std::recursive_mutex::try_lock</a>. */ BOOL OSTryLockMutex(OSMutex *mutex) { auto thread = OSGetCurrentThread(); internal::lockScheduler(); internal::testThreadCancelNoLock(); if (mutex->owner && mutex->owner != thread) { // Someone else owns this mutex internal::unlockScheduler(); return FALSE; } if (mutex->owner != thread) { // Add to mutex queue MutexQueue::append(&thread->mutexQueue, mutex); } mutex->owner = thread; mutex->count++; internal::unlockScheduler(); return TRUE; }
static void unlockMutexNoLock(OSMutex *mutex) { auto thread = OSGetCurrentThread(); decaf_check(mutex->tag == OSMutex::Tag); decaf_check(mutex->owner == thread); decaf_check(mutex->count > 0); mutex->count--; if (mutex->count == 0) { mutex->owner = nullptr; // If we have a promoted priority, reset it. if (thread->priority < thread->basePriority) { thread->priority = internal::calculateThreadPriorityNoLock(thread); } // Remove mutex from thread's mutex queue MutexQueue::erase(&thread->mutexQueue, mutex); // Wakeup any threads trying to lock this mutex internal::wakeupThreadNoLock(&mutex->queue); } }
void OSSleepTicks(OSTime ticks) { auto thread = OSGetCurrentThread(); OSLockScheduler(); // Create the alarm user data auto data = OSAllocFromSystem<SleepAlarmData>(); data->thread = thread; // Create an alarm to trigger wakeup auto alarm = OSAllocFromSystem<OSAlarm>(); OSCreateAlarm(alarm); OSSetAlarmUserData(alarm, data); OSSetAlarm(alarm, ticks, pSleepAlarmHandler); // Sleep thread OSSleepThreadNoLock(nullptr); OSRescheduleNoLock(); OSFreeToSystem(data); OSFreeToSystem(alarm); OSUnlockScheduler(); }
void OSSetThreadSpecific(uint32_t id, uint32_t value) { OSGetCurrentThread()->specific[id] = value; }