Ejemplo n.º 1
0
/**
 * 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;
   }
}
Ejemplo n.º 2
0
/**
 * 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();
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
/**
 * 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;
}
Ejemplo n.º 5
0
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();
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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;
   }
}
Ejemplo n.º 8
0
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);
   }
}
Ejemplo n.º 9
0
BOOL
OSSetThreadCancelState(BOOL state)
{
   auto thread = OSGetCurrentThread();
   auto old = thread->cancelState;
   thread->cancelState = state;
   return old;
}
Ejemplo n.º 10
0
void
sleepThreadNoLock(OSThreadQueue *queue)
{
   auto thread = OSGetCurrentThread();
   thread->queue = queue;
   thread->state = OSThreadState::Waiting;

   if (queue) {
      ThreadQueue::insert(queue, thread);
   }
}
Ejemplo n.º 11
0
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;
}
Ejemplo n.º 12
0
/**
 * 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();
}
Ejemplo n.º 13
0
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);
      }
   }
}
Ejemplo n.º 14
0
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;
   }
}
Ejemplo n.º 15
0
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;
}
Ejemplo n.º 16
0
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();
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
/**
 * 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;
}
Ejemplo n.º 19
0
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;
}
Ejemplo n.º 20
0
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();
   }
}
Ejemplo n.º 21
0
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;
   }
}
Ejemplo n.º 22
0
/**
 * 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();
}
Ejemplo n.º 23
0
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++;
}
Ejemplo n.º 24
0
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;
}
Ejemplo n.º 25
0
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++;
}
Ejemplo n.º 26
0
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();
}
Ejemplo n.º 27
0
/**
 * 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;
}
Ejemplo n.º 28
0
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);
   }
}
Ejemplo n.º 29
0
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();
}
Ejemplo n.º 30
0
void
OSSetThreadSpecific(uint32_t id, uint32_t value)
{
   OSGetCurrentThread()->specific[id] = value;
}