mirror of https://github.com/zeldaret/botw.git
286 lines
6.4 KiB
C++
286 lines
6.4 KiB
C++
#include "KingSystem/Utils/Thread/Task.h"
|
|
#include <thread/seadThread.h>
|
|
#include "KingSystem/Utils/Thread/TaskQueueBase.h"
|
|
#include "KingSystem/Utils/Thread/TaskQueueLock.h"
|
|
#include "KingSystem/Utils/Thread/TaskThread.h"
|
|
|
|
namespace ksys::util {
|
|
|
|
TaskDelegateSetter::TaskDelegateSetter() = default;
|
|
|
|
TaskDelegateSetter::~TaskDelegateSetter() = default;
|
|
|
|
void TaskDelegateSetter::setDelegate(TaskDelegate* delegate) {
|
|
mDelegate = delegate;
|
|
}
|
|
|
|
Task::Task(sead::Heap* heap) : mEvent(heap, true) {
|
|
mEvent.setSignal();
|
|
}
|
|
|
|
Task::Task(sead::Heap* heap, sead::IDisposer::HeapNullOption heap_null_option)
|
|
: mEvent(heap, heap_null_option, true) {
|
|
mEvent.setSignal();
|
|
}
|
|
|
|
Task::~Task() {
|
|
finalize_();
|
|
}
|
|
|
|
void Task::deleteDelegate_() {
|
|
if (mDelegate && mFlags.isOn(Flag::DeleteDelegate) && mFlags.isOff(Flag::DoNotDeleteDelegate)) {
|
|
::operator delete(mDelegate);
|
|
mDelegate = nullptr;
|
|
}
|
|
}
|
|
|
|
// NON_MATCHING: mDelegate2 = nullptr store
|
|
void Task::finalize_() {
|
|
if (mStatus == Status::Finalized)
|
|
return;
|
|
|
|
removeFromQueue();
|
|
deleteDelegate_();
|
|
mUserData = nullptr;
|
|
mQueue = nullptr;
|
|
mPostRunCallback = nullptr;
|
|
mRemoveCallback = nullptr;
|
|
mStatus = Status::Finalized;
|
|
}
|
|
|
|
bool Task::setDelegate(const TaskDelegateSetter& setter) {
|
|
mDelegate = setter.getDelegate();
|
|
mFlags.reset(Flag::DeleteDelegate);
|
|
mFlags.reset(Flag::DoNotDeleteDelegate);
|
|
mFlags.set(Flag::DoNotDeleteDelegate);
|
|
return onSetDelegate_(setter);
|
|
}
|
|
|
|
// NON_MATCHING: branching
|
|
bool Task::submitRequest(TaskRequest& request) {
|
|
// Processing this request is impossible if there is no thread *and* no queue!
|
|
if (request.mThread == nullptr && request.mQueue == nullptr)
|
|
return false;
|
|
|
|
if (!canSubmitRequest())
|
|
return false;
|
|
|
|
if (request.mSynchronous || request.mHasHandle)
|
|
mFlags.set(Flag::NeedsToSignalEvent);
|
|
else
|
|
mFlags.reset(Flag::NeedsToSignalEvent);
|
|
|
|
mFlags.change(Flag::SynchronousRequest, request.mSynchronous);
|
|
|
|
if (mListNode.isLinked())
|
|
return false;
|
|
|
|
if (mFlags.isOn(Flag::NeedsToSignalEvent))
|
|
mEvent.resetSignal();
|
|
|
|
mQueue = request.mQueue;
|
|
if (!mQueue) {
|
|
mQueue = request.mThread->getTaskQueue();
|
|
request.mQueue = mQueue;
|
|
}
|
|
mUserData = request.mUserData;
|
|
if (request.mDelegate)
|
|
setDelegateInternal_(request.mDelegate);
|
|
mRemoveCallback = request.mRemoveCallback;
|
|
mPostRunCallback = request.mPostRunCallback;
|
|
mName = request.mName;
|
|
|
|
prepare_(&request);
|
|
|
|
if (request.mSynchronous) {
|
|
auto* thread = mQueue->getCurrentThread();
|
|
if (thread) {
|
|
processOnCurrentThreadDirectly(thread);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
TaskQueueBase::PushArg arg;
|
|
arg.lane_id = request.mLaneId;
|
|
arg.task = this;
|
|
const bool push_ok = mQueue->push(arg);
|
|
bool b;
|
|
if (push_ok) {
|
|
if (request.mSynchronous)
|
|
mEvent.wait();
|
|
b = true;
|
|
} else {
|
|
b = false;
|
|
}
|
|
return push_ok || b;
|
|
}
|
|
|
|
bool Task::canSubmitRequest() const {
|
|
const bool run_finished_on_current_thread =
|
|
mThread && mStatus == Status::RunFinished &&
|
|
mThread == sead::ThreadMgr::instance()->getCurrentThread();
|
|
const bool cond2 = isInactive();
|
|
return run_finished_on_current_thread || cond2;
|
|
}
|
|
|
|
void Task::setUserData(void* user_data) {
|
|
mUserData = user_data;
|
|
}
|
|
|
|
bool Task::setDelegateInternal_(TaskDelegate* delegate) {
|
|
deleteDelegate_();
|
|
mFlags.set(Flag::DoNotDeleteDelegate);
|
|
mDelegate = delegate;
|
|
return delegate != nullptr;
|
|
}
|
|
|
|
void Task::processOnCurrentThreadDirectly(TaskThread* thread) {
|
|
{
|
|
TaskQueueLock lock{thread};
|
|
mThread = thread;
|
|
mStatus = Status::Pushed;
|
|
}
|
|
|
|
run();
|
|
|
|
{
|
|
TaskQueueLock lock{thread};
|
|
onRunFinished();
|
|
}
|
|
|
|
TaskPostRunResult result;
|
|
invokePostRunCallback(&result);
|
|
|
|
{
|
|
TaskQueueLock lock{thread};
|
|
if (!result.getResult())
|
|
finish();
|
|
}
|
|
}
|
|
|
|
void Task::removeFromQueue() {
|
|
if (mQueue)
|
|
mQueue->removeTask(this, true);
|
|
}
|
|
|
|
void Task::removeFromQueue2() {
|
|
// TODO: how does this differ from removeFromQueue?
|
|
removeFromQueue();
|
|
}
|
|
|
|
void Task::run_() {
|
|
if (mDelegate)
|
|
mDelegateResult = mDelegate->invoke(mUserData);
|
|
}
|
|
|
|
bool Task::wait() {
|
|
mEvent.wait();
|
|
return true;
|
|
}
|
|
|
|
bool Task::wait(const sead::TickSpan& span) {
|
|
return mEvent.wait(span);
|
|
}
|
|
|
|
u8 Task::getLaneId() const {
|
|
return mLaneId;
|
|
}
|
|
|
|
bool Task::isSuccess() const {
|
|
return (mStatus == Status::PreFinishCallback || mStatus == Status::RunFinished ||
|
|
mStatus == Status::PostFinishCallback) &&
|
|
mDelegateResult;
|
|
}
|
|
|
|
bool Task::isInactive() const {
|
|
return mStatus == Status::Uninitialized || mStatus == Status::RemovedFromQueue ||
|
|
mStatus == Status::PostFinishCallback;
|
|
}
|
|
|
|
void Task::setStatusPushed() {
|
|
mStatus = Status::Pushed;
|
|
}
|
|
|
|
void Task::setThread(TaskThread* thread) {
|
|
mThread = thread;
|
|
}
|
|
|
|
void Task::run() {
|
|
run_();
|
|
mStatus = Status::RunFinished;
|
|
}
|
|
|
|
void Task::onRunFinished() {
|
|
onRunFinished_();
|
|
}
|
|
|
|
void Task::invokePostRunCallback(TaskPostRunResult* result) {
|
|
mRemoveCallback = nullptr;
|
|
TaskPostRunContext context;
|
|
context.mCancelled = mFlags.isOn(Flag::Cancelled);
|
|
context.mTask = this;
|
|
context.mUserData = mUserData;
|
|
if (auto* delegate = mPostRunCallback) {
|
|
mPostRunCallback = nullptr;
|
|
delegate->invoke(result, context);
|
|
}
|
|
}
|
|
|
|
void Task::finish() {
|
|
mStatus = Status::PreFinishCallback;
|
|
onFinish_();
|
|
mStatus = Status::PostFinishCallback;
|
|
|
|
mThread = nullptr;
|
|
signalEvent();
|
|
|
|
onPostFinish_();
|
|
}
|
|
|
|
void* Task::getUserData() const {
|
|
return mUserData;
|
|
}
|
|
|
|
void Task::cancel() {
|
|
mEvent.resetSignal();
|
|
mFlags.set(Flag::Cancelled);
|
|
mFlags.set(Flag::NeedsToSignalEvent);
|
|
}
|
|
|
|
void Task::onRemove() {
|
|
invokeRemoveCallback_();
|
|
|
|
TaskQueueLock lock;
|
|
mQueue->lock(&lock);
|
|
|
|
preRemove_();
|
|
|
|
mStatus = Status::RemovedFromQueue;
|
|
mThread = nullptr;
|
|
signalEvent();
|
|
|
|
postRemove_();
|
|
}
|
|
|
|
void Task::invokeRemoveCallback_() {
|
|
mPostRunCallback = nullptr;
|
|
TaskRemoveCallbackContext context;
|
|
context.mTask = this;
|
|
context.mUserData = mUserData;
|
|
|
|
if (auto* delegate = mRemoveCallback) {
|
|
mRemoveCallback = nullptr;
|
|
delegate->invoke(context);
|
|
}
|
|
}
|
|
|
|
void Task::setStatusFetched() {
|
|
mStatus = Status::Fetched;
|
|
}
|
|
|
|
void Task::setLaneId(u8 id) {
|
|
mLaneId = id;
|
|
}
|
|
|
|
} // namespace ksys::util
|