/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: WorkPairImpl.hpp Date: 2021-11-1 Author: Reece ***/ #pragma once namespace Aurora::Threading::Primitives { class SpinLoop; } namespace Aurora::Async { struct BasicWorkStdFunc; /// @hideinitializer struct BasicWorkCtx : WorkPriv { BasicWorkCtx() { magic = AuConvertMagicTag32("BWOT"); opt = nullptr; } void *opt; }; /// @hideinitializer template, typename Job_t = FJob> struct WorkPairImpl : IWorkItemHandler, AuEnableSharedFromThis { WorkPairImpl() : caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(Task_t &&task) : task(AuMove(task)), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(Task_t &&task, Job_t &&callback) : task(AuMove(task)), callback(AuMove(callback)), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(const Task_t &task) : task(task), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(const Task_t &task, const Job_t &callback) : task(task), callback(callback), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(const Task_t &task, const Job_t &callback, const Info_t &info) : task(task), callback(callback), input(info), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(Task_t &&task, const Job_t &callback, const Info_t &info) : task(AuMove(task)), callback(callback), input(info), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(Task_t &&task, Job_t &&callback, const Info_t &info) : task(AuMove(task)), callback(AuMove(callback)), input(info), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(Task_t &&task, Job_t &&callback, Info_t &&info) : task(AuMove(task)), callback(AuMove(callback)), input(AuMove(info)), caller_(Async::GetCurrentWorkerPId()) {} WorkPairImpl(const Task_t &task, const Job_t &callback, Info_t &&info) : task(task), callback(callback), input(info), caller_(Async::GetCurrentWorkerPId()) {} Info_t input; Task_t task; Job_t callback; WorkPairImpl &SetTask(const Task_t &task) { this->task = task; return *this; } WorkPairImpl &SetTask(const Job_t &callback) { this->callback = callback; return *this; } private: static constexpr bool IsCallbackPtr = AuIsPointer_v || AuIsBaseOfTemplate::value || AuIsBaseOfTemplate::value; static constexpr bool IsTaskPtr = AuIsPointer_v || AuIsBaseOfTemplate::value || AuIsBaseOfTemplate::value; //WorkerId_t caller; WorkerPId_t caller_; Threading::Primitives::SpinLock lock_; BasicWorkCtx secretContext_; Result_t resultValue_; virtual void *GetPrivateData() override { return &secretContext_; } void DispatchFrame(ProcessInfo &info) override { AU_LOCK_GUARD(this->lock_); try { if constexpr (IsTaskPtr) { if (task) { resultValue_ = task->OnFrame(input); } } else { resultValue_ = task.OnFrame(input); } task = {}; } catch (...) { Debug::PrintError(); ShutdownNoLock(); return; } auto pin = AuSharedFromThis(); AuFunction func = [pin]() { try { pin->secretContext_.opt = &pin->resultValue_; if constexpr (IsCallbackPtr) { pin->callback->onSuccess(pin->input, pin->resultValue_); } else { if (pin->callback.onSuccess) { pin->callback.onSuccess(pin->input,pin->resultValue_); } } } catch (...) { Debug::PrintError(); } pin->callback = {}; pin->caller_ = {}; }; try { if (caller_ == Async::GetCurrentWorkerPId()) { func(); } else { AuFunction err = [pin]() { pin->CallOnFailure(); }; // TODO: this is somewhat evil. double alloc when we could reuse this if (!caller_.pool->NewWorkItem(caller_, AuMakeShared(func, err))->Dispatch()) { pin->CallOnFailure(); } } caller_ = {}; } catch (...) { Debug::PrintError(); ShutdownNoLock(); } } void Shutdown() override { AU_LOCK_GUARD(this->lock_); ShutdownNoLock(); } inline void ShutdownNoLock() { caller_ = {}; try { CallOnFailure(); } catch (...) { Debug::PrintError(); } callback = {}; task = {}; } inline void CallOnFailure() { if constexpr (IsCallbackPtr) { if constexpr (AuIsBaseOfTemplateonFailure)>::value) { if (!callback->onFailure) { return; } } callback->onFailure(input); } else { if constexpr (AuIsBaseOfTemplate::value) { if (!callback.onFailure) { return; } } callback.onFailure(input); } } }; }