summaryrefslogtreecommitdiff
path: root/src/KingSystem/Utils/Thread/TaskMgr.cpp
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2020-09-15 19:13:52 +0200
committerLéo Lam <leo@leolam.fr>2020-09-16 17:49:37 +0200
commit3db6228dfc65a25da49bab26ab8bcf4abc69a98f (patch)
tree3946a9211c2adc7213b1ae94ccf4baca22b73020 /src/KingSystem/Utils/Thread/TaskMgr.cpp
parent8b7369dffb1e816b2f27ab0133d2bac218383a45 (diff)
ksys: Add more task utilities
* TaskMgr, ManagedTask, ManagedTaskHandle * GameTaskThread: partial implementation because PhysicsMemSys / Havok stuff hasn't been decompiled yet and calc_() requires PhysicsMemSys
Diffstat (limited to 'src/KingSystem/Utils/Thread/TaskMgr.cpp')
-rw-r--r--src/KingSystem/Utils/Thread/TaskMgr.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/KingSystem/Utils/Thread/TaskMgr.cpp b/src/KingSystem/Utils/Thread/TaskMgr.cpp
new file mode 100644
index 00000000..b5eec563
--- /dev/null
+++ b/src/KingSystem/Utils/Thread/TaskMgr.cpp
@@ -0,0 +1,212 @@
+#include "KingSystem/Utils/Thread/TaskMgr.h"
+#include <heap/seadHeap.h>
+#include <heap/seadHeapMgr.h>
+#include "KingSystem/Utils/Thread/ManagedTask.h"
+#include "KingSystem/Utils/Thread/TaskThread.h"
+
+namespace ksys::util {
+
+TaskMgr::TaskMgr(sead::Heap* heap)
+ : mTasksCS(heap), mCS2(heap), mNewFreeTaskEvent(heap, true), mEvent2(heap, true) {
+ mFreeTaskLists[0].initOffset(ManagedTask::getListNodeOffset());
+ mFreeTaskLists[1].initOffset(ManagedTask::getListNodeOffset());
+ mNewFreeTaskEvent.resetSignal();
+}
+
+TaskMgr::~TaskMgr() {
+ finalize();
+}
+
+void TaskMgr::finalize() {
+ if (mFlags.isOn(Flag::HeapIsFreeable)) {
+ if (mTask)
+ delete mTask;
+ mTask = nullptr;
+
+ for (auto*& task : mTasks) {
+ if (task)
+ delete task;
+ task = nullptr;
+ }
+
+ mTasks.freeBuffer();
+
+ } else {
+ if (mTask) {
+ mTask->~ManagedTask();
+ mTask = nullptr;
+ }
+
+ for (auto* task : mTasks)
+ task->~ManagedTask();
+ }
+}
+
+void TaskMgr::submitRequest(TaskMgrRequest& request) {
+ bool request_had_no_task = false;
+ if (!request.task) {
+ request_had_no_task = true;
+ fetchIdleTaskForRequest_(request, true);
+ }
+
+ if (!request.task)
+ return;
+
+ auto* task = request.task;
+ if (request.handle) {
+ auto* queue = request.request->mQueue;
+ if (!queue) {
+ auto* thread = request.request->mThread;
+ queue = thread ? thread->getTaskQueue() : nullptr;
+ }
+ task->attachHandle(request.handle, queue);
+ } else {
+ task->attachHandle(nullptr, nullptr);
+ }
+
+ const bool ok = task->submitRequest(*request.request);
+ if (!ok) {
+ if (auto* managed_task = sead::DynamicCast<ManagedTask>(request.task)) {
+ managed_task->attachHandle(nullptr, nullptr);
+ if (!request_had_no_task)
+ return;
+ freeTask(managed_task);
+ } else if (!request_had_no_task) {
+ return;
+ }
+ }
+
+ if (request_had_no_task || !ok)
+ request.task = nullptr;
+}
+
+// NON_MATCHING: reorderings
+bool TaskMgr::fetchIdleTaskForRequest_(TaskMgrRequest& request, bool retry_until_success) {
+ if (!hasTasks())
+ return false;
+
+ ManagedTask* task = [this, retry_until_success] {
+ const auto lock1 = sead::makeScopedLock(mTasksCS);
+ if (auto* task = fetchIdleTask_(retry_until_success))
+ return task;
+
+ swapLists_();
+ if (!retry_until_success)
+ return fetchIdleTask_(retry_until_success);
+
+ while (true) {
+ if (auto* task_1 = fetchIdleTask_(retry_until_success))
+ return task_1;
+ mNewFreeTaskEvent.wait();
+ swapLists_();
+ }
+ }();
+
+ if (!task)
+ return false;
+
+ request.task = task;
+ return true;
+}
+
+void TaskMgr::freeTask(ManagedTask* task) {
+ auto lock = sead::makeScopedLock(mCS2);
+ mFreeTaskLists[getListIndex2_()].pushBack(task);
+ mNewFreeTaskEvent.setSignal();
+}
+
+bool TaskMgr::trySubmitRequest(TaskMgrRequest& request) {
+ bool request_had_no_task = false;
+ if (!request.task) {
+ request_had_no_task = true;
+ if (!tryFetchTaskForRequest_(request, false))
+ return false;
+ }
+
+ if (!request.task)
+ return false;
+
+ auto* task = request.task;
+ if (request.handle) {
+ auto* queue = request.request->mQueue;
+ if (!queue) {
+ auto* thread = request.request->mThread;
+ queue = thread ? thread->getTaskQueue() : nullptr;
+ }
+ task->attachHandle(request.handle, queue);
+ } else {
+ task->attachHandle(nullptr, nullptr);
+ }
+
+ const bool ok = task->submitRequest(*request.request);
+ if (!ok) {
+ if (auto* managed_task = sead::DynamicCast<ManagedTask>(request.task)) {
+ managed_task->attachHandle(nullptr, nullptr);
+ if (!request_had_no_task)
+ return ok;
+ freeTask(managed_task);
+ } else if (!request_had_no_task) {
+ return ok;
+ }
+ }
+
+ if (request_had_no_task || !ok)
+ request.task = nullptr;
+ return ok;
+}
+
+// NON_MATCHING: the factory invoke function pointer is loaded earlier in the original code
+void TaskMgr::init(s32 num_tasks, sead::Heap* heap, ManagedTaskFactory& factory) {
+ if (!heap->isFreeable())
+ mFlags.reset(Flag::HeapIsFreeable);
+ else
+ mFlags.set(Flag::HeapIsFreeable);
+
+ const sead::ScopedCurrentHeapSetter heap_setter{heap};
+
+ mTasks.allocBufferAssert(num_tasks, heap);
+
+ if (mTasks.size() != 0) {
+ auto& list = mFreeTaskLists[mListIndex];
+ for (auto*& task : mTasks) {
+ factory(&task);
+ task->setMgr(this);
+ list.pushBack(task);
+ }
+ }
+
+ factory(&mTask);
+}
+
+bool TaskMgr::hasTasks() const {
+ return mTasks.size() > 0;
+}
+
+ManagedTask* TaskMgr::fetchIdleTask_(bool retry_until_success) {
+ auto lock = sead::makeScopedLock(mTasksCS);
+
+ if (mFreeTaskLists[mListIndex].isEmpty())
+ return nullptr;
+
+ ManagedTask* result = nullptr;
+ while (true) {
+ for (auto& task : mFreeTaskLists[mListIndex]) {
+ if (task.isIdle()) {
+ result = std::addressof(task);
+ break;
+ }
+ }
+
+ if (result) {
+ auto lock1 = sead::makeScopedLock(mTasksCS);
+ mFreeTaskLists[mListIndex].erase(result);
+ return result;
+ }
+
+ if (!retry_until_success)
+ return nullptr;
+ mEvent2.wait(sead::TickSpan::fromMilliSeconds(1));
+ }
+}
+
+} // namespace ksys::util