/** * 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; }
/** * 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); }
/** * Set alarm user data which is returned by OSGetAlarmUserData. */ void OSSetAlarmUserData(OSAlarm *alarm, void *data) { OSUninterruptibleSpinLock_Acquire(gAlarmLock); alarm->userData = data; OSUninterruptibleSpinLock_Release(gAlarmLock); }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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); }
/** * 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); }
/** * 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; }
/** * 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; }