Пример #1
0
/**
 * Sleep the current thread until the alarm has been triggered or cancelled.
 */
BOOL
OSWaitAlarm(OSAlarm *alarm)
{
    coreinit::internal::lockScheduler();
    OSUninterruptibleSpinLock_Acquire(gAlarmLock);
    BOOL result = FALSE;
    assert(alarm);
    assert(alarm->tag == OSAlarm::Tag);

    if (alarm->state != OSAlarmState::Set) {
        OSUninterruptibleSpinLock_Release(gAlarmLock);
        coreinit::internal::unlockScheduler();
        return FALSE;
    }

    coreinit::internal::sleepThreadNoLock(&alarm->threadQueue);
    OSUninterruptibleSpinLock_Release(gAlarmLock);
    coreinit::internal::rescheduleNoLock();

    OSUninterruptibleSpinLock_Acquire(gAlarmLock);

    if (alarm->state != OSAlarmState::Cancelled) {
        result = TRUE;
    }

    OSUninterruptibleSpinLock_Release(gAlarmLock);
    coreinit::internal::unlockScheduler();
    return result;
}
Пример #2
0
/**
 * Add a task to the end of the queue.
 *
 * Returns FALSE if the task state is not set to Initialised.
 * Returns FALSE if the task queue is full.
 * Returns FALSE if the task queue state is not valid.
 *
 * \return Returns TRUE if the task was added to the queue.
 */
BOOL
MPEnqueTask(MPTaskQueue *queue,
            MPTask *task)
{
   if (task->state != MPTaskState::Initialised) {
      return FALSE;
   }

   OSUninterruptibleSpinLock_Acquire(&queue->lock);

   if (queue->queueSize >= queue->queueMaxSize) {
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return FALSE;
   }

   if (queue->state < MPTaskQueueState::Initialised || queue->state > MPTaskQueueState::Finished) {
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return FALSE;
   }

   task->queue = queue;
   task->state = MPTaskState::Ready;

   queue->tasks++;
   queue->tasksReady++;
   queue->queue[queue->queueSize] = task;
   queue->queueSize++;

   if (queue->state == MPTaskQueueState::Finished) {
      queue->state = MPTaskQueueState::Ready;
   }

   OSUninterruptibleSpinLock_Release(&queue->lock);
   return TRUE;
}
Пример #3
0
/**
 * Resets the state of the task queue.
 *
 * This does not remove any tasks from the queue. It just resets the task queue
 * such that all tasks are ready to be run again.
 */
BOOL
MPResetTaskQ(MPTaskQueue *queue)
{
   OSUninterruptibleSpinLock_Acquire(&queue->lock);

   if (queue->state != MPTaskQueueState::Finished &&
       queue->state != MPTaskQueueState::Stopped) {
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return FALSE;
   }

   queue->state = MPTaskQueueState::Initialised;
   queue->tasks = queue->queueSize;
   queue->tasksReady = queue->queueSize;
   queue->tasksRunning = 0;
   queue->tasksFinished = 0;
   queue->queueIndex = 0;

   for (auto i = 0u; i < queue->tasks; ++i) {
      auto task = queue->queue[i];
      task->result = 0;
      task->coreID = CoreCount;
      task->duration = 0;
      task->state = MPTaskState::Ready;
   }

   OSUninterruptibleSpinLock_Release(&queue->lock);
   return TRUE;
}
Пример #4
0
/**
 * Starts a task queue.
 *
 * Sets the task state to Ready.
 *
 * \return Returns true if state was previously Initialised or Stopped.
 */
BOOL
MPStartTaskQ(MPTaskQueue *queue)
{
   OSUninterruptibleSpinLock_Acquire(&queue->lock);

   if (queue->state == MPTaskQueueState::Initialised || queue->state == MPTaskQueueState::Stopped) {
      queue->state = MPTaskQueueState::Ready;
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return TRUE;
   }

   OSUninterruptibleSpinLock_Release(&queue->lock);
   return FALSE;
}
Пример #5
0
/**
 * Set alarm user data which is returned by OSGetAlarmUserData.
 */
void
OSSetAlarmUserData(OSAlarm *alarm, void *data)
{
    OSUninterruptibleSpinLock_Acquire(gAlarmLock);
    alarm->userData = data;
    OSUninterruptibleSpinLock_Release(gAlarmLock);
}
Пример #6
0
/**
 * Set an alarm tag which is used in OSCancelAlarms for bulk cancellation.
 */
void
OSSetAlarmTag(OSAlarm *alarm, uint32_t group)
{
    OSUninterruptibleSpinLock_Acquire(gAlarmLock);
    alarm->group = group;
    OSUninterruptibleSpinLock_Release(gAlarmLock);
}
Пример #7
0
/**
 * Dequeue 1 task at queueIndex
 *
 * Does not remove tasks from queue buffer.
 *
 * \return Returns dequeued task.
 */
MPTask *
MPDequeTask(MPTaskQueue *queue)
{
   OSUninterruptibleSpinLock_Acquire(&queue->lock);

   if (queue->state != MPTaskQueueState::Ready
    || queue->queueIndex == queue->queueSize) {
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return nullptr;
   }

   auto task = queue->queue[queue->queueIndex];
   queue->queueIndex++;
   OSUninterruptibleSpinLock_Release(&queue->lock);
   return task;
}
Пример #8
0
/**
 * Run a specific task.
 *
 * The task must belong to a queue.
 * The task must be in the Ready state.
 *
 * \return Returns TRUE if task was run.
 */
BOOL
MPRunTask(virt_ptr<MPTask> task)
{
   auto queue = task->queue;

   if (task->state != MPTaskState::Ready) {
      return FALSE;
   }

   if (!queue
     || queue->state == MPTaskQueueState::Stopping
     || queue->state == MPTaskQueueState::Stopped) {
      return FALSE;
   }

   OSUninterruptibleSpinLock_Acquire(virt_addrof(queue->lock));
   queue->tasksReady--;
   queue->tasksRunning++;
   OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock));

   task->state = MPTaskState::Running;
   task->coreID = OSGetCoreId();

   auto start = OSGetTime();
   task->result = cafe::invoke(cpu::this_core::state(),
                               task->func,
                               task->userArg1,
                               task->userArg2);
   task->duration = OSGetTime() - start;

   task->state = MPTaskState::Finished;

   OSUninterruptibleSpinLock_Acquire(virt_addrof(queue->lock));
   queue->tasksRunning--;
   queue->tasksFinished++;

   if (queue->state == MPTaskQueueState::Stopping && queue->tasksRunning == 0) {
      queue->state = MPTaskQueueState::Stopped;
   }

   if (queue->tasks == queue->tasksFinished) {
      queue->state = MPTaskQueueState::Finished;
   }

   OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock));
   return TRUE;
}
Пример #9
0
/**
 * Stops a task queue.
 *
 * If there are tasks running the state is set to  Stopping.
 * If there are no tasks running the state is set to Stopped.
 *
 * \return Returns FALSE if the task queue is not in the Ready state.
 */
BOOL
MPStopTaskQ(MPTaskQueue *queue)
{
   OSUninterruptibleSpinLock_Acquire(&queue->lock);

   if (queue->state == MPTaskQueueState::Ready) {
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return FALSE;
   }

   if (queue->tasksRunning == 0) {
      queue->state = MPTaskQueueState::Stopped;
   } else {
      queue->state = MPTaskQueueState::Stopping;
   }

   OSUninterruptibleSpinLock_Release(&queue->lock);
   return TRUE;
}
Пример #10
0
/**
 * Stops a task queue.
 *
 * If there are tasks running the state is set to  Stopping.
 * If there are no tasks running the state is set to Stopped.
 *
 * \return Returns FALSE if the task queue is not in the Ready state.
 */
BOOL
MPStopTaskQ(virt_ptr<MPTaskQueue> queue)
{
   OSUninterruptibleSpinLock_Acquire(virt_addrof(queue->lock));

   if (queue->state != MPTaskQueueState::Ready) {
      OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock));
      return FALSE;
   }

   if (queue->tasksRunning == 0) {
      queue->state = MPTaskQueueState::Stopped;
   } else {
      queue->state = MPTaskQueueState::Stopping;
   }

   OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock));
   return TRUE;
}
Пример #11
0
/**
 * Get the status of a task queue.
 */
BOOL
MPGetTaskQInfo(MPTaskQueue *queue,
               MPTaskQueueInfo *info)
{
   OSUninterruptibleSpinLock_Acquire(&queue->lock);
   info->state = queue->state;
   info->tasks = queue->tasks;
   info->tasksReady = queue->tasksReady;
   info->tasksRunning = queue->tasksRunning;
   info->tasksFinished = queue->tasksFinished;
   OSUninterruptibleSpinLock_Release(&queue->lock);
   return TRUE;
}
Пример #12
0
/**
 * Get the status of a task queue.
 */
BOOL
MPGetTaskQInfo(virt_ptr<MPTaskQueue> queue,
               virt_ptr<MPTaskQueueInfo> info)
{
   OSUninterruptibleSpinLock_Acquire(virt_addrof(queue->lock));
   info->state = queue->state;
   info->tasks = queue->tasks;
   info->tasksReady = queue->tasksReady;
   info->tasksRunning = queue->tasksRunning;
   info->tasksFinished = queue->tasksFinished;
   OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock));
   return TRUE;
}
Пример #13
0
/**
 * Dequeue N tasks from queueIndex
 *
 * Does not remove tasks from queue buffer.
 *
 * \return Returns number of tasks dequeued.
 */
uint32_t
MPDequeTasks(MPTaskQueue *queue,
             be_ptr<MPTask> *taskBuffer,
             uint32_t taskBufferLen)
{
   OSUninterruptibleSpinLock_Acquire(&queue->lock);
   uint32_t count, available;

   if (queue->state != MPTaskQueueState::Ready) {
      OSUninterruptibleSpinLock_Release(&queue->lock);
      return 0;
   }

   available = queue->queueSize - queue->queueIndex;
   count = std::min(available, taskBufferLen);

   for (auto i = 0u; i < count; ++i) {
      taskBuffer[i] = queue->queue[queue->queueIndex];
      queue->queueIndex++;
   }

   OSUninterruptibleSpinLock_Release(&queue->lock);
   return count;
}
Пример #14
0
/**
 * Internal check to see if any alarms are ready to be triggered.
 *
 * Updates the processor internal interrupt timer to trigger for the next ready alarm.
 */
void
checkAlarms(uint32_t core, OSContext *context)
{
    auto queue = gAlarmQueue[core];
    auto now = OSGetTime();
    auto next = std::chrono::time_point<std::chrono::system_clock>::max();

    coreinit::internal::lockScheduler();
    OSUninterruptibleSpinLock_Acquire(gAlarmLock);

    // Trigger all pending alarms
    for (OSAlarm *alarm = queue->head; alarm; ) {
        auto nextAlarm = alarm->link.next;

        if (alarm->nextFire <= now && alarm->state != OSAlarmState::Cancelled) {
            triggerAlarmNoLock(queue, alarm, context);
        }

        alarm = nextAlarm;
    }

    // Find next set alarm
    for (OSAlarm *alarm = queue->head; alarm; ) {
        auto nextAlarm = alarm->link.next;

        if (alarm->state == OSAlarmState::Set && alarm->nextFire) {
            auto nextFire = coreinit::internal::toTimepoint(alarm->nextFire);

            if (nextFire < next) {
                next = nextFire;
            }
        }

        alarm = nextAlarm;
    }

    OSUninterruptibleSpinLock_Release(gAlarmLock);
    coreinit::internal::unlockScheduler();
    gProcessor.setInterruptTimer(core, next);
}
Пример #15
0
/**
 * Internal alarm handler.
 *
 * Resets the alarm state.
 * Calls the users callback.
 * Wakes up any threads waiting on the alarm.
 */
static void
triggerAlarmNoLock(OSAlarmQueue *queue, OSAlarm *alarm, OSContext *context)
{
    alarm->context = context;

    if (alarm->period) {
        alarm->nextFire = OSGetTime() + alarm->period;
        alarm->state = OSAlarmState::Set;
    } else {
        alarm->nextFire = 0;
        alarm->state = OSAlarmState::None;
        alarm->alarmQueue = nullptr;
        OSEraseFromQueue<OSAlarmQueue>(queue, alarm);
    }

    if (alarm->callback) {
        OSUninterruptibleSpinLock_Release(gAlarmLock);
        alarm->callback(alarm, context);
        OSUninterruptibleSpinLock_Acquire(gAlarmLock);
    }

    coreinit::internal::wakeupThreadNoLock(&alarm->threadQueue);
}
Пример #16
0
/**
 * Run N tasks from queue.
 *
 * Does not remove tasks from queue.
 * Can be run from multiple threads at once.
 *
 * Side Effects:
 * - Sets state to Stopped if state is Stopping and tasksRunning reaches 0.
 * - Sets state to Finished if all tasks are finished.
 * - TasksReady -> TasksRunning -> TasksFinished.
 *
 * Returns TRUE if at least 1 task is run.
 */
BOOL
MPRunTasksFromTaskQ(MPTaskQueue *queue,
                    uint32_t tasks)
{
   BOOL result = FALSE;

   while (queue->state == MPTaskQueueState::Ready) {
      uint32_t first, count, available;

      OSUninterruptibleSpinLock_Acquire(&queue->lock);
      available = queue->queueSize - queue->queueIndex;
      count = std::min(available, tasks);
      first = queue->queueIndex;
      tasks -= count;

      queue->tasksReady -= count;
      queue->tasksRunning += count;
      queue->queueIndex += count;
      OSUninterruptibleSpinLock_Release(&queue->lock);

      if (count == 0) {
         // Nothing to run, lets go home!
         break;
      }

      // Result is TRUE if at least 1 task is run
      result = TRUE;

      // Mark all tasks as running
      for (auto i = 0u; i < count; ++i) {
         auto task = queue->queue[first + i];
         task->state = MPTaskState::Running;
         task->coreID = OSGetCoreId();
      }

      // Run all tasks
      for (auto i = 0u; i < count; ++i) {
         auto task = queue->queue[first + i];
         auto start = OSGetTime();
         task->result = task->func(task->userArg1, task->userArg2);
         task->state = MPTaskState::Finished;
         task->duration = OSGetTime() - start;
      }

      OSUninterruptibleSpinLock_Acquire(&queue->lock);
      queue->tasksRunning -= count;
      queue->tasksFinished += count;

      if (queue->state == MPTaskQueueState::Stopping && queue->tasksRunning == 0) {
         queue->state = MPTaskQueueState::Stopped;
      }

      if (queue->tasks == queue->tasksFinished) {
         queue->state = MPTaskQueueState::Finished;
      }

      OSUninterruptibleSpinLock_Release(&queue->lock);
   }

   return result;
}