mirror of
https://github.com/LNH-team/pico-launcher.git
synced 2026-06-02 00:56:55 +02:00
Initial commit
This commit is contained in:
39
arm9/source/core/task/Task.cpp
Normal file
39
arm9/source/core/task/Task.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "common.h"
|
||||
#include "Task.h"
|
||||
|
||||
void TaskBase::Execute()
|
||||
{
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
if (_state == TaskState::NotStarted)
|
||||
{
|
||||
_state = TaskState::Running;
|
||||
rtos_restoreIrqs(irqs);
|
||||
TaskState finalState = ExecuteDirect();
|
||||
SetFinalState(finalState);
|
||||
}
|
||||
else
|
||||
rtos_restoreIrqs(irqs);
|
||||
}
|
||||
|
||||
void TaskBase::SetFinalState(TaskState finalState)
|
||||
{
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
if (_state == TaskState::Running)
|
||||
{
|
||||
_state = finalState;
|
||||
rtos_wakeupQueue(&_threadQueue);
|
||||
}
|
||||
rtos_restoreIrqs(irqs);
|
||||
}
|
||||
|
||||
bool TaskBase::Wait()
|
||||
{
|
||||
u32 irq = rtos_disableIrqs();
|
||||
|
||||
if (!IsCompleted())
|
||||
rtos_queueThread(rtos_getCurThread(), &_threadQueue);
|
||||
|
||||
rtos_restoreIrqs(irq);
|
||||
|
||||
return IsCompletedSuccessfully();
|
||||
}
|
||||
89
arm9/source/core/task/Task.h
Normal file
89
arm9/source/core/task/Task.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include "TaskResult.h"
|
||||
|
||||
class TaskBase
|
||||
{
|
||||
public:
|
||||
virtual ~TaskBase() { }
|
||||
|
||||
void Execute();
|
||||
|
||||
TaskState GetState() const { return _state; }
|
||||
|
||||
bool IsCompleted() const { return _state >= TaskState::Completed; }
|
||||
bool IsCanceled() const { return _state == TaskState::Canceled; }
|
||||
bool IsFailed() const { return _state == TaskState::Failed; }
|
||||
bool IsCompletedSuccessfully() const { return _state == TaskState::Completed; }
|
||||
|
||||
bool Wait();
|
||||
|
||||
bool GetDestroyWhenComplete() const { return _destroyWhenComplete; }
|
||||
void SetDestroyWhenComplete() { _destroyWhenComplete = true; }
|
||||
|
||||
void RequestCancel() { _cancelRequested = true; }
|
||||
bool IsCancelRequested() const { return _cancelRequested; }
|
||||
|
||||
protected:
|
||||
volatile u8 _cancelRequested = false;
|
||||
|
||||
virtual TaskState ExecuteDirect() = 0;
|
||||
|
||||
private:
|
||||
rtos_thread_queue_t _threadQueue = { NULL };
|
||||
volatile TaskState _state = TaskState::NotStarted;
|
||||
volatile u8 _destroyWhenComplete = false;
|
||||
|
||||
void SetFinalState(TaskState finalState);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Task : public TaskBase
|
||||
{
|
||||
public:
|
||||
const T& GetResult() const { return this->_result; }
|
||||
|
||||
protected:
|
||||
virtual TaskResult<T> ExecuteFunc() const = 0;
|
||||
|
||||
private:
|
||||
T _result;
|
||||
|
||||
TaskState ExecuteDirect()
|
||||
{
|
||||
auto result = ExecuteFunc();
|
||||
if (result.GetFinalState() == TaskState::Completed)
|
||||
this->_result = result.GetResult();
|
||||
return result.GetFinalState();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class Task<void> : public TaskBase
|
||||
{
|
||||
protected:
|
||||
virtual TaskResult<void> ExecuteFunc() const = 0;
|
||||
|
||||
private:
|
||||
TaskState ExecuteDirect() override
|
||||
{
|
||||
auto result = ExecuteFunc();
|
||||
return result.GetFinalState();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, typename FuncType>
|
||||
class FuncTask : public Task<T>
|
||||
{
|
||||
public:
|
||||
FuncTask(const FuncType& function)
|
||||
: _function(function) { }
|
||||
|
||||
private:
|
||||
const FuncType _function;
|
||||
|
||||
TaskResult<T> ExecuteFunc() const override { return _function((const volatile u8&)this->_cancelRequested); }
|
||||
};
|
||||
27
arm9/source/core/task/TaskFactory.h
Normal file
27
arm9/source/core/task/TaskFactory.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Task.h"
|
||||
|
||||
class TaskFactory
|
||||
{
|
||||
template <typename T>
|
||||
static T TaskResultToResultType(TaskResult<T> arg);
|
||||
|
||||
public:
|
||||
template <typename FuncType>
|
||||
static auto Create(const FuncType& function)
|
||||
{
|
||||
return FuncTask<decltype(TaskResultToResultType(function())), FuncType>(function);
|
||||
}
|
||||
|
||||
template <typename FuncType>
|
||||
static auto CreateOnHeap(const FuncType& function) -> Task<decltype(TaskResultToResultType(function()))>*
|
||||
{
|
||||
return new FuncTask<decltype(TaskResultToResultType(function())), FuncType>(function);
|
||||
}
|
||||
|
||||
template <typename FuncType>
|
||||
static auto CreateOnHeapUnique(const FuncType& function) -> std::unique_ptr<Task<decltype(TaskResultToResultType(function()))>>
|
||||
{
|
||||
return std::make_unique<FuncTask<decltype(TaskResultToResultType(function())), FuncType>>(function);
|
||||
}
|
||||
};
|
||||
43
arm9/source/core/task/TaskQueue.cpp
Normal file
43
arm9/source/core/task/TaskQueue.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "common.h"
|
||||
#include "TaskQueue.h"
|
||||
|
||||
void TaskQueueBase::ThreadMain(TaskBase** queue, u32 queueLength)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_idle = false;
|
||||
u32 readPtr = _queueReadPtr;
|
||||
while (readPtr != _queueWritePtr)
|
||||
{
|
||||
TaskBase* task = queue[readPtr];
|
||||
if (readPtr == queueLength - 1)
|
||||
readPtr = 0;
|
||||
else
|
||||
readPtr++;
|
||||
_queueReadPtr = readPtr;
|
||||
if (!task)
|
||||
continue;
|
||||
task->Execute();
|
||||
if (task->GetDestroyWhenComplete())
|
||||
{
|
||||
// this will destroy the task
|
||||
ReturnOwnership(task);
|
||||
}
|
||||
}
|
||||
if (_endThreadWhenDone)
|
||||
return;
|
||||
_idle = true;
|
||||
rtos_waitEvent(&_event, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueTaskBase::Dispose()
|
||||
{
|
||||
if (_task)
|
||||
{
|
||||
TaskBase* task = _task;
|
||||
_task = nullptr;
|
||||
_taskQueue->ReturnOwnership(task);
|
||||
_taskQueue = nullptr;
|
||||
}
|
||||
}
|
||||
212
arm9/source/core/task/TaskQueue.h
Normal file
212
arm9/source/core/task/TaskQueue.h
Normal file
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include "core/BitVector.h"
|
||||
#include "Task.h"
|
||||
|
||||
class TaskQueueBase;
|
||||
|
||||
class QueueTaskBase
|
||||
{
|
||||
public:
|
||||
// forbid copies
|
||||
QueueTaskBase(const QueueTaskBase&) = delete;
|
||||
QueueTaskBase& operator=(const QueueTaskBase&) = delete;
|
||||
|
||||
// move assignment
|
||||
QueueTaskBase& operator=(QueueTaskBase&& other)
|
||||
{
|
||||
_taskQueue = other._taskQueue;
|
||||
_task = other._task;
|
||||
other._taskQueue = nullptr;
|
||||
other._task = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~QueueTaskBase()
|
||||
{
|
||||
// extra check here for optimizing out the dispose call after move assignment
|
||||
if (_task)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
void Dispose();
|
||||
|
||||
void CancelTask()
|
||||
{
|
||||
if (_task)
|
||||
{
|
||||
_task->RequestCancel();
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsValid() const { return _task != nullptr; }
|
||||
|
||||
protected:
|
||||
TaskBase* _task;
|
||||
|
||||
QueueTaskBase(TaskBase* task, TaskQueueBase* taskQueue)
|
||||
: _task(task), _taskQueue(taskQueue) { }
|
||||
|
||||
private:
|
||||
TaskQueueBase* _taskQueue;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class QueueTask : public QueueTaskBase
|
||||
{
|
||||
public:
|
||||
QueueTask()
|
||||
: QueueTaskBase(nullptr, nullptr) { }
|
||||
|
||||
QueueTask(Task<T>* task, TaskQueueBase* taskQueue)
|
||||
: QueueTaskBase(task, taskQueue) { }
|
||||
|
||||
const Task<T>& GetTask() const { return (const Task<T>&)*_task; }
|
||||
};
|
||||
|
||||
class TaskQueueBase
|
||||
{
|
||||
protected:
|
||||
template <typename T>
|
||||
static T TaskResultToResultType(TaskResult<T> arg);
|
||||
|
||||
public:
|
||||
virtual void ReturnOwnership(TaskBase* task) = 0;
|
||||
|
||||
template <typename FuncType>
|
||||
[[gnu::noinline]]
|
||||
auto Enqueue(const FuncType& function) -> QueueTask<decltype(TaskResultToResultType(function(*new vu8())))>
|
||||
{
|
||||
using TaskType = FuncTask<decltype(TaskResultToResultType(function(*new vu8()))), FuncType>;
|
||||
// static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool");
|
||||
void* slot = GetSlot();
|
||||
auto task = new (slot) TaskType(function);
|
||||
Enqueue(task);
|
||||
return QueueTask(task, this);
|
||||
}
|
||||
|
||||
protected:
|
||||
rtos_event_t _event;
|
||||
vu32 _queueReadPtr = 0;
|
||||
vu32 _queueWritePtr = 0;
|
||||
volatile bool _endThreadWhenDone = false;
|
||||
volatile bool _idle = true;
|
||||
|
||||
void ThreadMain(TaskBase** queue, u32 queueLength);
|
||||
|
||||
TaskQueueBase()
|
||||
{
|
||||
rtos_createEvent(&_event);
|
||||
}
|
||||
|
||||
virtual void* GetSlot() = 0;
|
||||
virtual void Enqueue(TaskBase* task) = 0;
|
||||
};
|
||||
|
||||
template <u32 QueueLength, u32 MaxTaskSize>
|
||||
class TaskQueue : public TaskQueueBase
|
||||
{
|
||||
public:
|
||||
using TaskQueueBase::Enqueue;
|
||||
|
||||
~TaskQueue()
|
||||
{
|
||||
StopThread();
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void ReturnOwnership(TaskBase* task) override
|
||||
{
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
if (task->IsCompleted())
|
||||
{
|
||||
task->~TaskBase();
|
||||
u32 slot = ((u32)task - (u32)_taskPool) / ((MaxTaskSize + 3) & ~3);
|
||||
_poolOccupation.Set(slot, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
task->SetDestroyWhenComplete();
|
||||
}
|
||||
rtos_restoreIrqs(irqs);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void StartThread(u8 threadPriority, u32* stack, u32 stackSize)
|
||||
{
|
||||
if (_threadStarted)
|
||||
return;
|
||||
_endThreadWhenDone = false;
|
||||
_threadStarted = true;
|
||||
rtos_createThread(&_thread, threadPriority, ThreadMain, this, stack, stackSize);
|
||||
rtos_wakeupThread(&_thread);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void StopThread()
|
||||
{
|
||||
if (!_threadStarted)
|
||||
return;
|
||||
_endThreadWhenDone = true;
|
||||
rtos_signalEvent(&_event);
|
||||
rtos_joinThread(&_thread);
|
||||
_threadStarted = false;
|
||||
}
|
||||
|
||||
bool IsIdle() const
|
||||
{
|
||||
return _queueReadPtr == _queueWritePtr && _idle;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 _taskPool[QueueLength][(MaxTaskSize + 3) / 4];
|
||||
BitVector<QueueLength> _poolOccupation;
|
||||
TaskBase* _queue[QueueLength];
|
||||
rtos_thread_t _thread;
|
||||
bool _threadStarted = false;
|
||||
|
||||
[[gnu::noinline]]
|
||||
u32 AcquireSlot()
|
||||
{
|
||||
u32 slot;
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
{
|
||||
slot = _poolOccupation.FindFirstZero();
|
||||
_poolOccupation.Set(slot, 1);
|
||||
}
|
||||
rtos_restoreIrqs(irqs);
|
||||
LOG_DEBUG("AcquireSlot: %d\n", slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
void* GetSlot() override
|
||||
{
|
||||
return _taskPool[AcquireSlot()];
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void Enqueue(TaskBase* task) override
|
||||
{
|
||||
u32 writePtr = _queueWritePtr;
|
||||
_queue[writePtr] = task;
|
||||
if (writePtr == QueueLength - 1)
|
||||
_queueWritePtr = 0;
|
||||
else
|
||||
_queueWritePtr = writePtr + 1;
|
||||
rtos_signalEvent(&_event);
|
||||
}
|
||||
|
||||
static void ThreadMain(void* arg)
|
||||
{
|
||||
((TaskQueue*)arg)->ThreadMain();
|
||||
}
|
||||
|
||||
void ThreadMain()
|
||||
{
|
||||
TaskQueueBase::ThreadMain(&_queue[0], QueueLength);
|
||||
}
|
||||
};
|
||||
39
arm9/source/core/task/TaskResult.h
Normal file
39
arm9/source/core/task/TaskResult.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "TaskState.h"
|
||||
|
||||
template <class T>
|
||||
class TaskResult
|
||||
{
|
||||
const TaskState _finalState;
|
||||
const T _result;
|
||||
|
||||
TaskResult(TaskState finalState)
|
||||
: _finalState(finalState) { }
|
||||
|
||||
TaskResult(const T& result)
|
||||
: _finalState(TaskState::Completed), _result(result) { }
|
||||
|
||||
public:
|
||||
static TaskResult<T> Failed() { return TaskResult<T>(TaskState::Failed); }
|
||||
static TaskResult<T> Canceled() { return TaskResult<T>(TaskState::Canceled); }
|
||||
static TaskResult<T> Completed(const T& result) { return TaskResult<T>(result); }
|
||||
|
||||
TaskState GetFinalState() const { return _finalState; }
|
||||
const T& GetResult() const { return _result; }
|
||||
};
|
||||
|
||||
template <>
|
||||
class TaskResult<void>
|
||||
{
|
||||
const TaskState _finalState;
|
||||
|
||||
TaskResult(TaskState finalState)
|
||||
: _finalState(finalState) { }
|
||||
|
||||
public:
|
||||
static TaskResult<void> Failed() { return TaskResult<void>(TaskState::Failed); }
|
||||
static TaskResult<void> Canceled() { return TaskResult<void>(TaskState::Canceled); }
|
||||
static TaskResult<void> Completed() { return TaskResult<void>(TaskState::Completed); }
|
||||
|
||||
TaskState GetFinalState() const { return _finalState; }
|
||||
};
|
||||
10
arm9/source/core/task/TaskState.h
Normal file
10
arm9/source/core/task/TaskState.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
enum class TaskState : u8
|
||||
{
|
||||
NotStarted,
|
||||
Running,
|
||||
Completed,
|
||||
Canceled,
|
||||
Failed
|
||||
};
|
||||
Reference in New Issue
Block a user