A pretty large patch not worth breaking up into separate commits
[*] Split up Aurora Async [*] Split Async app into seperate ThreadPool concept [*] Fix various OSThread bugs and tls transfer issues [*] Set default affinity to 0xFFFFFFFF [*] Update Build script [+] Add AuTuplePopFront [+] New Network Interface (unimplemented) [*] Stub out the interfaces required for a better logger [*] Fix Win32 ShellExecute bug; windows 11 struggles without explicit com init per the docs - now deferring to thread pool [*] Update gitignore [*] Follow XDG home standard [*] Refactor some namespaces to use the shorthand aliases [*] Various stability fixesmaster
parent
4109852d06
commit
99c5e1fa65
|
@ -1,26 +1,208 @@
|
|||
# Aurora's general purpose JS/TS/C/C++/Go vs/vscode/intellij/codelite gitignore reference
|
||||
# Almost usable for Java and Qt
|
||||
|
||||
# Aurora build configuration
|
||||
Build_CompilerWorkingDirectory/*
|
||||
Build_Developers/*
|
||||
Build_Ship/*
|
||||
Build_Internal/*
|
||||
Build_Develop/*
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
Build_Stage/*
|
||||
Build_Ship/*
|
||||
Build_Workspace/*
|
||||
|
||||
# License Headers VS extension
|
||||
*.licenseheader
|
||||
|
||||
# Binaries / object files
|
||||
*.dll
|
||||
*.exe
|
||||
*.obj
|
||||
*.so
|
||||
*.so.*
|
||||
*.la
|
||||
*.lai
|
||||
*.pdb
|
||||
*.idb
|
||||
*.exe~
|
||||
*.obj
|
||||
*.dynlib
|
||||
*.dylib
|
||||
*.lib
|
||||
*.d
|
||||
*.o
|
||||
*.a
|
||||
*.la
|
||||
*.slo
|
||||
*.lo
|
||||
*.out
|
||||
.vs
|
||||
# go unit test
|
||||
*.test
|
||||
|
||||
# Autogenerated project files
|
||||
compile_flags.txt
|
||||
*.mk
|
||||
*.project
|
||||
*cmake
|
||||
Makefile
|
||||
*.vcxproj
|
||||
*.xcodeproj
|
||||
|
||||
# IDE trash
|
||||
.vscode
|
||||
.vs
|
||||
/*.gcno
|
||||
.intellij
|
||||
.clion
|
||||
Makefile
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
*.tlog
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
xcuserdata/
|
||||
|
||||
# Win32
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
*.lnk
|
||||
|
||||
# Linux is trash and cant hotswap like NT
|
||||
.nfs*
|
||||
.fuse_hidden*
|
||||
|
||||
# Ninja
|
||||
.ninja_deps
|
||||
.ninja_log
|
||||
|
||||
# PID locks
|
||||
*.pid
|
||||
*.pid.lock
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# JetBrains
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Android Studio
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# why would we ever ship this dir?
|
||||
.idea/caches/*
|
||||
|
||||
# NodeJS
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# VS Code Extensions
|
||||
.vscode-test
|
||||
|
||||
# Qt unit tests
|
||||
target_wrapper.*
|
||||
|
||||
# QtCreator
|
||||
*.autosave
|
||||
|
||||
# QtCreator Qml
|
||||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
|
||||
# QtCreator CMake
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# QtCreator 4.8< compilation database
|
||||
compile_commands.json
|
||||
|
||||
# QtCreator local machine specific files for imported projects
|
||||
*creator.user*
|
||||
|
||||
*_qmlcache.qrc
|
||||
|
||||
# QT cache and user files
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
|
||||
# Java trash
|
||||
hs_err_pid*
|
||||
.gradle
|
||||
gradle-app.setting
|
||||
!gradle-wrapper.jar
|
||||
.gradletasknamecache
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
|
@ -7,8 +7,8 @@
|
|||
"staticImpDefines": "AURORA_ENGINE_KERNEL_STATIC",
|
||||
"defines": [],
|
||||
"soft-depends": ["wxwidgets", "glm"],
|
||||
"depends": ["AuroraInterfaces", "mimalloc", "uuid", "fmt", "json", "bzip2", "ltc", "o1heap", "zstd", "zlib", "lz4", "mbedtls"],
|
||||
"include-depends": ["fmt", "uuid", "AuroraInterfaces"],
|
||||
"depends": ["AuroraInterfaces", "AuroraEnum", "mimalloc", "uuid", "fmt", "json", "bzip2", "ltc", "o1heap", "zstd", "zlib", "lz4", "mbedtls"],
|
||||
"include-depends": ["fmt", "uuid", "AuroraInterfaces", "AuroraEnum"],
|
||||
"features": ["guess-platform-code"],
|
||||
"linkSources": "Source/Alloc.cpp",
|
||||
"actions": [
|
||||
|
@ -19,4 +19,4 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,305 +7,52 @@
|
|||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Loop
|
||||
{
|
||||
class ILoopSource;
|
||||
}
|
||||
#include "AsyncTypes.hpp"
|
||||
|
||||
#include "IWorkItem.hpp"
|
||||
#include "IWorkItemHandler.hpp"
|
||||
#include "IThreadPool.hpp"
|
||||
#include "IAsyncApp.hpp"
|
||||
|
||||
#include "Jobs.hpp"
|
||||
#include "Tasks.hpp"
|
||||
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
class IWorkItem;
|
||||
class IAsyncApp;
|
||||
AUKN_SYM IAsyncApp *GetAsyncApp();
|
||||
|
||||
struct AVoid
|
||||
{
|
||||
AuUInt8 x[1];
|
||||
};
|
||||
|
||||
AUKN_SYM IAsyncApp *GetAsyncApp();
|
||||
|
||||
/// ThreadGroup_t:
|
||||
/// 0 = system main thread
|
||||
/// 1+ = user defined
|
||||
using ThreadGroup_t = AuUInt8;
|
||||
|
||||
/// ThreadId_t:
|
||||
/// -1 = invalid
|
||||
/// index = tid/runner id
|
||||
using ThreadId_t = AuUInt16;
|
||||
|
||||
static const ThreadId_t kThreadIdAny = -1;
|
||||
|
||||
struct WorkerId_t : AuPair<ThreadGroup_t, ThreadId_t>
|
||||
{
|
||||
WorkerId_t() : AuPair<ThreadGroup_t, ThreadId_t>(0, 0)
|
||||
{}
|
||||
|
||||
WorkerId_t(ThreadGroup_t group) : AuPair<ThreadGroup_t, ThreadId_t>(group, kThreadIdAny)
|
||||
{}
|
||||
|
||||
WorkerId_t(ThreadGroup_t group, ThreadId_t id) : AuPair<ThreadGroup_t, ThreadId_t>(group, id)
|
||||
{}
|
||||
|
||||
WorkerId_t(const WorkerId_t &cpy) : AuPair<ThreadGroup_t, ThreadId_t>(cpy.first, cpy.second)
|
||||
{}
|
||||
};
|
||||
|
||||
struct WorkPriv
|
||||
{
|
||||
AuUInt32 magic;
|
||||
};
|
||||
|
||||
struct IWorkItemHandler
|
||||
{
|
||||
enum class EProcessNext
|
||||
{
|
||||
eInvalid = -1,
|
||||
eFinished = 0,
|
||||
eRerun,
|
||||
eSchedule,
|
||||
eFailed
|
||||
};
|
||||
|
||||
struct ProcessInfo
|
||||
{
|
||||
ProcessInfo(bool finished) : type(finished ? EProcessNext::eFinished : EProcessNext::eFailed) {}
|
||||
ProcessInfo(EProcessNext type) : type(type) {}
|
||||
ProcessInfo(const AuList<AuSPtr<IWorkItem>> &blockedBy) : type(EProcessNext::eSchedule), waitFor(blockedBy) {}
|
||||
// ...
|
||||
|
||||
EProcessNext type;
|
||||
AuList<AuSPtr<IWorkItem>> waitFor;
|
||||
AuUInt32 reschedMs;
|
||||
AuUInt64 reschedNs;
|
||||
};
|
||||
|
||||
virtual void DispatchFrame(ProcessInfo &info) = 0;
|
||||
|
||||
/// A really terrible name for the overloadable method that serves as the critical failure callback
|
||||
/// You have a 'shutdown'/free function you can overload, it's called the dtor
|
||||
/// Don't moan about the shitty naming of this, im not refactoring it
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void *GetPrivateData() { return nullptr; }
|
||||
};
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct FJob
|
||||
{
|
||||
std::function<void(const Info_t &, const Result_t &)> onSuccess = 0;
|
||||
std::function<void(const Info_t &)> onFailure = 0;
|
||||
};
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromConsumer(const AuConsumer<const Info_t &, const Result_t &> &onSuccess)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(in, a);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromConsumer(const AuConsumer<const Info_t &, const Result_t &> &onSuccess, const AuConsumer<const Info_t &, bool/*neverDispatched*/> &onFailure)
|
||||
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(in, a);
|
||||
};
|
||||
ret.onFailure = [=](const Info_t &a)
|
||||
{
|
||||
onFailure(a, true);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromConsumer(const AuConsumer<const Info_t &, const Result_t &> &onSuccess, const AuConsumer<const Info_t &> &onFailure)
|
||||
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(in, a);
|
||||
};
|
||||
ret.onFailure = [=](const Info_t &a)
|
||||
{
|
||||
onFailure(a);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid, class BaseInfo_t = AVoid, class BaseResult_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromDerivedJob(const FJob<BaseInfo_t, BaseResult_t> &reference)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
if (reference.onSuccess)
|
||||
{
|
||||
reference.onSuccess(in, a);
|
||||
}
|
||||
};
|
||||
ret.onFailure = [=](const Info_t &a)
|
||||
{
|
||||
if (reference.onFailure)
|
||||
{
|
||||
reference.onSuccess(a);
|
||||
}
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
using FVoidJob = FJob<AVoid, AVoid>;
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct CJob
|
||||
{
|
||||
void(* onSuccess)(const Info_t &, const Result_t &);
|
||||
void(* onFailure)(const Info_t &);
|
||||
};
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct FTask
|
||||
{
|
||||
std::function<Result_t(const Info_t &)> onFrame = 0;
|
||||
};
|
||||
|
||||
using FVoidTask = FTask<AVoid, AVoid>;
|
||||
|
||||
template<typename Info_t = AVoid, typename Out_t = AVoid, class ClazzImpl>
|
||||
FTask<Info_t, Out_t> TaskFromConsumerRefT(ClazzImpl &&func)
|
||||
{
|
||||
FTask<Info_t, Out_t> ret;
|
||||
ret.onFrame = [callable = func](const Info_t &in) -> Out_t
|
||||
{
|
||||
if constexpr (std::is_same_v<Out_t, AVoid>)
|
||||
{
|
||||
callable(in);
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return callable(in);
|
||||
}
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Out_t = AVoid>
|
||||
FTask<Info_t, Out_t> TaskFromVoidVoid(const AuVoidFunc &func)
|
||||
{
|
||||
FTask<Info_t, Out_t> ret;
|
||||
ret.onFrame = [callable = func](const Info_t &in) -> Out_t
|
||||
{
|
||||
callable();
|
||||
return {};
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct CTask
|
||||
{
|
||||
Result_t(* onFrame)(const Info_t &);
|
||||
};
|
||||
|
||||
class IWorkItem
|
||||
{
|
||||
public:
|
||||
virtual AuSPtr<IWorkItem> WaitFor(const AuSPtr<IWorkItem> &workItem) = 0;
|
||||
virtual AuSPtr<IWorkItem> WaitFor(const AuList<AuSPtr<IWorkItem>> &workItem) = 0;
|
||||
|
||||
// ms = time relative to the current time
|
||||
virtual AuSPtr<IWorkItem> SetSchedTime(AuUInt32 ms) = 0;
|
||||
|
||||
// ns = time relative to the current time
|
||||
virtual AuSPtr<IWorkItem> SetSchedTimeNs(AuUInt64 ns) = 0;
|
||||
|
||||
// ms = time relative to the time at which the work item would otherwise dispatch
|
||||
virtual AuSPtr<IWorkItem> AddDelayTime(AuUInt32 ms) = 0;
|
||||
|
||||
// ns = time relative to the time at which the work item would otherwise dispatch
|
||||
virtual AuSPtr<IWorkItem> AddDelayTimeNs(AuUInt64 ns) = 0;
|
||||
|
||||
virtual AuSPtr<IWorkItem> Then(const AuSPtr<IWorkItem> &next) = 0;
|
||||
|
||||
virtual AuSPtr<IWorkItem> Dispatch() = 0;
|
||||
|
||||
virtual bool BlockUntilComplete() = 0;
|
||||
virtual bool HasFinished() = 0;
|
||||
virtual bool HasFailed() = 0;
|
||||
virtual void Cancel() = 0;
|
||||
|
||||
virtual void *GetPrivateData() = 0;
|
||||
virtual AuOptional<void *> ToWorkResultT() = 0;
|
||||
};
|
||||
///
|
||||
AUKN_SYM WorkerPId_t GetCurrentWorkerPId();
|
||||
|
||||
/// Async app only | Thread pools must use the IThreadPool::NewFence function
|
||||
AUKN_SYM AuSPtr<IWorkItem> NewWorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking = false);
|
||||
|
||||
AUKN_SYM AuSPtr<IWorkItem> NewWorkItem(const WorkerPId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking = false);
|
||||
|
||||
/// Async app only | Thread pools must use the IThreadPool::NewFence function
|
||||
AUKN_SYM AuSPtr<IWorkItem> NewFence();
|
||||
|
||||
class IAsyncApp
|
||||
{
|
||||
public:
|
||||
// Main thread logic
|
||||
virtual void Start() = 0;
|
||||
virtual void Main() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual bool Exiting() = 0;
|
||||
virtual void SetConsoleCommandDispatcher(WorkerId_t id) = 0;
|
||||
/// Allocates a new thread pool for usage
|
||||
AUKN_SYM AuSPtr<IThreadPool> NewThreadPool();
|
||||
|
||||
// Spawning
|
||||
virtual bool Spawn(WorkerId_t workerId) = 0;
|
||||
|
||||
// Event runner threads release upon encountering a zero work condition allowing for a clean exit of event driven apps without the headache of carefully chaining together exit callbacks
|
||||
// Applications that aren't designed around an event driven model should set callerOwns to true to keep the around during global work exhaustion
|
||||
virtual void SetWorkerIdIsThreadRunner(WorkerId_t, bool runner) = 0;
|
||||
|
||||
virtual Threading::Threads::ThreadShared_t ResolveHandle(WorkerId_t) = 0;
|
||||
virtual AuBST<ThreadGroup_t, AuList<ThreadId_t>> GetThreads() = 0;
|
||||
virtual WorkerId_t GetCurrentThread() = 0;
|
||||
|
||||
// Synchronization
|
||||
// Note: syncing to yourself will nullify requireSignal to prevent deadlock
|
||||
virtual bool Sync(WorkerId_t group, AuUInt32 timeoutMs = 0, bool requireSignal = false) = 0;
|
||||
virtual void Signal(WorkerId_t group) = 0;
|
||||
virtual void SyncAllSafe() = 0;
|
||||
|
||||
// Features
|
||||
virtual void AddFeature(WorkerId_t id, AuSPtr<Threading::Threads::IThreadFeature> feature, bool async = false) = 0;
|
||||
|
||||
// Debug
|
||||
virtual void AssertInThreadGroup(ThreadGroup_t thread) = 0;
|
||||
virtual void AssertWorker(WorkerId_t id) = 0;
|
||||
|
||||
virtual bool Poll(bool block) = 0;
|
||||
|
||||
virtual bool ScheduleLoopSource(const AuSPtr<Loop::ILoopSource> &loopSource, WorkerId_t workerId, AuUInt32 timeout, const AuConsumer<AuSPtr<Loop::ILoopSource>, bool> &callback) = 0;
|
||||
};
|
||||
|
||||
|
||||
// TODO: move the following trash out of here
|
||||
#pragma region EASE_OF_READING
|
||||
struct BasicWorkStdFunc : IWorkItemHandler
|
||||
{
|
||||
std::function<void()> callback;
|
||||
std::function<void()> shutdown; // error
|
||||
AuFunction<void()> callback;
|
||||
AuFunction<void()> shutdown; // error
|
||||
|
||||
BasicWorkStdFunc(std::function<void()> &&callback, std::function<void()> &&shutdown) : callback(std::move(callback)), shutdown(std::move(shutdown))
|
||||
BasicWorkStdFunc(AuFunction<void()> &&callback, AuFunction<void()> &&shutdown) : callback(std::move(callback)), shutdown(std::move(shutdown))
|
||||
{}
|
||||
|
||||
BasicWorkStdFunc(std::function<void()> &&callback) : callback(std::move(callback))
|
||||
BasicWorkStdFunc(AuFunction<void()> &&callback) : callback(std::move(callback))
|
||||
{}
|
||||
|
||||
BasicWorkStdFunc(const std::function<void()> &callback) : callback(callback)
|
||||
BasicWorkStdFunc(const AuFunction<void()> &callback) : callback(callback)
|
||||
{}
|
||||
|
||||
BasicWorkStdFunc(const std::function<void()> &callback, const std::function<void()> &shutdown) : callback(callback), shutdown(shutdown)
|
||||
BasicWorkStdFunc(const AuFunction<void()> &callback, const AuFunction<void()> &shutdown) : callback(callback), shutdown(shutdown)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
@ -315,7 +62,7 @@ namespace Aurora::Async
|
|||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
|
@ -330,7 +77,7 @@ namespace Aurora::Async
|
|||
{
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
|
@ -341,214 +88,9 @@ namespace Aurora::Async
|
|||
|
||||
|
||||
#if !defined(_CPPSHARP)
|
||||
|
||||
/// @hideinitializer
|
||||
struct BasicWorkCtx : WorkPriv
|
||||
{
|
||||
BasicWorkCtx()
|
||||
{
|
||||
magic = AuConvertMagicTag32("BWOT");
|
||||
opt = nullptr;
|
||||
}
|
||||
void *opt;
|
||||
};
|
||||
|
||||
/// @hideinitializer
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
struct BasicWorkCallback : IWorkItemHandler, std::enable_shared_from_this<IWorkItemHandler>
|
||||
{
|
||||
BasicWorkCallback()
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(Task_t &&task) : task(std::move(task))
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(Task_t &&task, Job_t &&callback) : task(std::move(task)), callback(std::move(callback))
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(const Task_t &task) : task(task)
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(const Task_t &task, const Job_t &callback) : task(task), callback(callback)
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(const Task_t &task, const Job_t &callback, const Info_t &info) : task(task), callback(callback), input(info)
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(Task_t &&task, const Job_t &callback, const Info_t &info) : task(std::move(task)), callback(callback), input(info)
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(Task_t &&task, Job_t &&callback, const Info_t &info) : task(std::move(task)), callback(std::move(callback)), input(info)
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(Task_t &&task, Job_t &&callback, Info_t &&info) : task(std::move(task)), callback(std::move(callback)), input(std::move(info))
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
BasicWorkCallback(const Task_t &task, const Job_t &callback, Info_t &&info) : task(task), callback(callback), input(info)
|
||||
{
|
||||
caller = GetAsyncApp()->GetCurrentThread();
|
||||
}
|
||||
|
||||
Info_t input;
|
||||
Task_t task;
|
||||
Job_t callback;
|
||||
|
||||
|
||||
BasicWorkCallback<Info_t, Result_t, Task_t, Job_t> &SetTask(const Task_t &task)
|
||||
{
|
||||
this->task = task;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BasicWorkCallback<Info_t, Result_t, Task_t, Job_t> &SetTask(const Job_t &callback)
|
||||
{
|
||||
this->callback = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static constexpr bool IsCallbackPtr = std::is_pointer_v<Job_t> || AuIsBaseOfTemplate<std::shared_ptr, Job_t>::value;
|
||||
static constexpr bool IsTaskPtr = std::is_pointer_v<Task_t> || AuIsBaseOfTemplate<std::shared_ptr, Task_t>::value;
|
||||
|
||||
WorkerId_t caller;
|
||||
|
||||
BasicWorkCtx secretContext_;
|
||||
Result_t resultValue_;
|
||||
|
||||
virtual void *GetPrivateData() override { return &secretContext_; }
|
||||
|
||||
void DispatchFrame(ProcessInfo &info) override
|
||||
{
|
||||
try
|
||||
{
|
||||
if constexpr (IsTaskPtr)
|
||||
{
|
||||
resultValue_ = task->onFrame(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultValue_ = task.onFrame(input);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
Shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
auto pin = std::static_pointer_cast<std::remove_pointer_t<decltype(this)>>(this->shared_from_this());
|
||||
|
||||
std::function<void()> 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();
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
if (caller == GetAsyncApp()->GetCurrentThread())
|
||||
{
|
||||
func();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::function<void()> err = [pin]()
|
||||
{
|
||||
pin->CallOnFailure();
|
||||
};
|
||||
|
||||
// TODO: this is somewhat evil. double alloc when we could reuse this
|
||||
if (!NewWorkItem(caller, AuMakeShared<BasicWorkStdFunc>(func, err))->Dispatch())
|
||||
{
|
||||
pin->CallOnFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() override
|
||||
{
|
||||
try
|
||||
{
|
||||
CallOnFailure();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
}
|
||||
}
|
||||
|
||||
void CallOnFailure()
|
||||
{
|
||||
if constexpr (IsCallbackPtr)
|
||||
{
|
||||
if constexpr (AuIsBaseOfTemplate<std::function, decltype(callback->onFailure)>::value)
|
||||
{
|
||||
if (!callback->onFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback->onFailure(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AuIsBaseOfTemplate<std::function, decltype(callback.onFailure)>::value)
|
||||
{
|
||||
if (!callback.onFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback.onFailure(input);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// @hideinitializer
|
||||
template<typename Frame_t = std::function<void()>, typename Cleanup_t = std::function<void()>>
|
||||
template<typename Frame_t = AuFunction<void()>, typename Cleanup_t = AuFunction<void()>>
|
||||
struct WorkItemCallable : IWorkItemHandler
|
||||
{
|
||||
Frame_t frame;
|
||||
|
@ -581,12 +123,13 @@ namespace Aurora::Async
|
|||
cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define ASYNC_ERROR(exp) { if constexpr (std::is_same_v<T, bool>) { SysPushErrorGen(exp); return {}; } else { throw std::string(exp); } }
|
||||
#define ASYNC_FINISH { if constexpr (std::is_same_v<T, bool>) { return true; } }
|
||||
|
||||
template<typename T = void, typename... Args, AU_TEMPLATE_ENABLE_WHEN(std::is_same_v<T, bool> || std::is_void<T>::value)>
|
||||
static std::function<T(Args&&...)> TranslateAsyncFunctionToDispatcherWithThread(WorkerId_t id, std::function<void(Args...)> func)
|
||||
static AuFunction<T(Args&&...)> TranslateAsyncFunctionToDispatcherWithThread(WorkerId_t id, AuFunction<void(Args...)> func)
|
||||
{
|
||||
if (!func) return {};
|
||||
return [=](Args&&... in) -> T
|
||||
|
@ -594,26 +137,28 @@ namespace Aurora::Async
|
|||
auto work = AuMakeShared<BasicWorkStdFunc>([=]() -> void {
|
||||
func(in...);
|
||||
});
|
||||
if (!work) ASYNC_ERROR("can't dispatch async call; out of memory");
|
||||
if (!work) ASYNC_ERROR("can't dispatch async call. out of memory");
|
||||
auto workItem = NewWorkItem(id, work);
|
||||
if (!workItem) ASYNC_ERROR("can't dispatch async call; out of memory");
|
||||
if (!workItem) ASYNC_ERROR("can't dispatch async call. out of memory");
|
||||
workItem->Dispatch();
|
||||
ASYNC_FINISH;
|
||||
};
|
||||
}
|
||||
|
||||
/// Async app only
|
||||
template<typename T = void, typename... Args, AU_TEMPLATE_ENABLE_WHEN(std::is_same_v<T, bool> || std::is_void<T>::value)>
|
||||
static std::function<T(Args&&...)> TranslateAsyncFunctionToDispatcher(std::function<void(Args...)> func)
|
||||
static AuFunction<T(Args&&...)> TranslateAsyncFunctionToDispatcher(AuFunction<void(Args...)> func)
|
||||
{
|
||||
return TranslateAsyncFunctionToDispatcherWithThread(GetAsyncApp()->GetCurrentThread(), func);
|
||||
}
|
||||
|
||||
/// Async app only
|
||||
template<typename B = void, typename T, typename... Args, AU_TEMPLATE_ENABLE_WHEN(std::is_same_v<T, bool> || std::is_void<T>::value)>
|
||||
static std::function<T(std::function<void(const B&)>, Args...)> TranslateAsyncReturnableFunctionToDispatcherWithThread(WorkerId_t id, std::function<B(Args...)> func)
|
||||
static AuFunction<T(AuFunction<void(const B&)>, Args...)> TranslateAsyncReturnableFunctionToDispatcherWithThread(WorkerId_t id, AuFunction<B(Args...)> func)
|
||||
{
|
||||
return [=](std::function<T(const B&)> callback, Args... in) -> T
|
||||
return [=](AuFunction<T(const B&)> callback, Args... in) -> T
|
||||
{
|
||||
auto work = AuMakeShared<BasicWorkCallback<AVoid, B>>();
|
||||
auto work = AuMakeShared<WorkPairImpl<AVoid, B>>();
|
||||
if (!work) ASYNC_ERROR("can't dispatch async call; out of memory");
|
||||
work.task.onProcess = [=](const AVoid &) -> B
|
||||
{
|
||||
|
@ -631,69 +176,6 @@ namespace Aurora::Async
|
|||
};
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> NewBasicWorkCallback(const WorkerId_t &worker, const Task_t &task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkItem(worker, AuMakeShared<BasicWorkCallback<Info_t, Result_t, Task_t>>(std::move(task), job), enableWait);
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> DispatchBasicWorkCallback(const WorkerId_t &worker, const Task_t &task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return NewBasicWorkCallback<Info_t, Result_t, Task_t, Job_t>(worker, task, job, enableWait)->Dispatch();
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> NewBasicWorkCallback(const WorkerId_t &worker, Task_t &&task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkItem(worker, AuMakeShared<BasicWorkCallback<Info_t, Result_t, Task_t>>(std::move(task), job), enableWait);
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> DispatchBasicWorkCallback(const WorkerId_t &worker, Task_t &&task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return NewBasicWorkCallback<Info_t, Result_t, Task_t, Job_t>(worker, std::move(task), job, enableWait)->Dispatch();
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> NewBasicWorkCallback(const WorkerId_t &worker, Task_t &&task, Job_t &&job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkItem(worker, AuMakeShared<BasicWorkCallback<Info_t, Result_t, Task_t>>(std::move(task), std::move(job)), enableWait);
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> DispatchBasicWorkCallback(const WorkerId_t &worker, Task_t &&task, Job_t &&job, bool enableWait = false)
|
||||
{
|
||||
return NewBasicWorkCallback<Info_t, Result_t, Task_t, Job_t>(worker, std::move(task), std::move(job), enableWait)->Dispatch();
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>>
|
||||
static AuSPtr<IWorkItem> DispatchBasicWorkCallback(const WorkerId_t &worker, const Task_t &task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
// TOOD: use faster object if job parguments are invalid
|
||||
// It would be nice if we didn't have to drag the job callback pair around with us
|
||||
return NewWorkItem(worker, AuMakeShared<BasicWorkCallback<Info_t, Result_t, Task_t>>(task, job, inputParameters), enableWait)->Dispatch();
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>, typename ClazzImpl>
|
||||
AuSPtr<IWorkItem> DispatchFunctional(const WorkerId_t &worker, ClazzImpl task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return DispatchBasicWorkCallback<Info_t, Result_t, Task_t, Job_t>(worker, TaskFromConsumerRefT<Info_t, Result_t>(task), job, inputParameters, enableWait);
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>, typename ClazzImpl>
|
||||
AuSPtr<IWorkItem> DispatchFunctor(const WorkerId_t &worker, ClazzImpl task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return DispatchBasicWorkCallback<Info_t, Result_t, Task_t, Job_t>(worker, TaskFromConsumerRefT<Info_t, Result_t>(task), job, inputParameters, enableWait);
|
||||
}
|
||||
|
||||
template<typename Info_t = AVoid, typename Result_t = AVoid, typename Task_t = FTask<Info_t, Result_t>, typename Job_t = FJob<Info_t, Result_t>, typename ClazzImpl>
|
||||
AuSPtr<IWorkItem> DispatchVoid(const WorkerId_t &worker, ClazzImpl task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return DispatchBasicWorkCallback<Info_t, Result_t, Task_t, Job_t>(worker, TaskFromVoidVoid<Info_t, Result_t>(task), job, inputParameters, enableWait);
|
||||
}
|
||||
|
||||
|
||||
#undef ASYNC_ERROR
|
||||
#undef ASYNC_FINISH
|
||||
|
||||
|
@ -701,3 +183,11 @@ namespace Aurora::Async
|
|||
|
||||
#pragma endregion EASE_OF_READING
|
||||
}
|
||||
|
||||
|
||||
#if !defined(_CPPSHARP)
|
||||
#include "JobFrom.hpp"
|
||||
#include "TaskFrom.hpp"
|
||||
#include "WorkPairImpl.hpp"
|
||||
#include "WorkBasic.hpp"
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AsyncTypes.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Loop
|
||||
{
|
||||
class ILoopSource;
|
||||
}
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IWorkItem;
|
||||
struct IAsyncApp;
|
||||
struct IThreadPool;
|
||||
|
||||
struct AVoid
|
||||
{
|
||||
AuUInt8 x[1];
|
||||
};
|
||||
|
||||
/// IAsyncApp:
|
||||
/// ThreadGroup_t:
|
||||
/// 0 = system main thread
|
||||
/// 1+ = user defined | user allocated thread
|
||||
using ThreadGroup_t = AuUInt8;
|
||||
|
||||
/// ThreadId_t:
|
||||
/// -1 = invalid or any
|
||||
/// index = tid/runner id
|
||||
using ThreadId_t = AuUInt16;
|
||||
|
||||
static const ThreadId_t kThreadIdAny = -1;
|
||||
|
||||
struct WorkerId_t : AuPair<ThreadGroup_t, ThreadId_t>
|
||||
{
|
||||
WorkerId_t() : AuPair<ThreadGroup_t, ThreadId_t>(0, 0)
|
||||
{}
|
||||
|
||||
WorkerId_t(ThreadGroup_t group) : AuPair<ThreadGroup_t, ThreadId_t>(group, kThreadIdAny)
|
||||
{}
|
||||
|
||||
WorkerId_t(ThreadGroup_t group, ThreadId_t id) : AuPair<ThreadGroup_t, ThreadId_t>(group, id)
|
||||
{}
|
||||
|
||||
WorkerId_t(const WorkerId_t &cpy) : AuPair<ThreadGroup_t, ThreadId_t>(cpy.first, cpy.second)
|
||||
{}
|
||||
};
|
||||
|
||||
struct WorkPriv
|
||||
{
|
||||
AuUInt32 magic;
|
||||
};
|
||||
|
||||
struct WorkerPId_t : WorkerId_t
|
||||
{
|
||||
WorkerPId_t()
|
||||
{}
|
||||
|
||||
WorkerPId_t(const AuSPtr<IThreadPool> &pool, ThreadGroup_t group) : WorkerId_t(group, kThreadIdAny), pool(pool)
|
||||
{}
|
||||
|
||||
WorkerPId_t(const AuSPtr<IThreadPool> &pool, ThreadGroup_t group, ThreadId_t id) : WorkerId_t(group, id), pool(pool)
|
||||
{}
|
||||
|
||||
WorkerPId_t(const AuSPtr<IThreadPool> &pool, const WorkerId_t &cpy) : WorkerId_t(cpy.first, cpy.second), pool(pool)
|
||||
{}
|
||||
|
||||
AuSPtr<IThreadPool> pool;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IAsyncApp.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IAsyncApp : IThreadPool
|
||||
{
|
||||
// Main thread logic
|
||||
|
||||
/// Initializes the async application before the hand-off
|
||||
virtual void Start() = 0;
|
||||
|
||||
/// Hand off
|
||||
virtual void Main() = 0;
|
||||
|
||||
/// Console config
|
||||
virtual void SetConsoleCommandDispatcher(WorkerId_t id) = 0;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IThreadPool.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IThreadPool
|
||||
{
|
||||
// Spawning
|
||||
/// @deprecated
|
||||
virtual bool Spawn(WorkerId_t workerId) = 0;
|
||||
|
||||
// Event runner threads release the entire application upon encountering a zero work condition allowing for a clean exit of event driven apps without the headache of carefully chaining together exit callbacks
|
||||
// Applications that aren't designed around an event driven model should set runner to false to keep the around during global work exhaustion
|
||||
// tl'dr: runner = true, async app; runner = false, thread pool
|
||||
virtual void SetRunningMode(bool eventRunning) = 0;
|
||||
|
||||
// Converts the current thread into an async thread with runner = false
|
||||
// Use with RunOnce, Poll
|
||||
virtual bool Create(WorkerId_t workerId) = 0;
|
||||
|
||||
//virtual bool CreateAndRun(WorkerId_t workerId) = 0;
|
||||
|
||||
// @returns any threads with runner = true
|
||||
virtual bool InRunnerMode() = 0;
|
||||
|
||||
// Manual execution (system thread and ::Create() users)
|
||||
virtual bool Poll() = 0;
|
||||
virtual bool RunOnce() = 0;
|
||||
virtual bool Run() = 0;
|
||||
|
||||
//
|
||||
virtual void Shutdown() = 0;
|
||||
virtual bool Exiting() = 0;
|
||||
|
||||
//
|
||||
virtual AuSPtr<IWorkItem> NewWorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking = false) = 0;
|
||||
virtual AuSPtr<IWorkItem> NewFence() = 0;
|
||||
|
||||
//
|
||||
virtual Threading::Threads::ThreadShared_t ResolveHandle(WorkerId_t) = 0;
|
||||
|
||||
//
|
||||
virtual AuBST<ThreadGroup_t, AuList<ThreadId_t>> GetThreads() = 0;
|
||||
|
||||
//
|
||||
virtual WorkerId_t GetCurrentThread() = 0;
|
||||
|
||||
// Synchronization
|
||||
// Note: syncing to yourself will nullify requireSignal to prevent deadlock
|
||||
virtual bool Sync(WorkerId_t workerId, AuUInt32 timeoutMs = 0, bool requireSignal = false) = 0;
|
||||
virtual void Signal(WorkerId_t workerId) = 0;
|
||||
virtual void SyncAllSafe() = 0;
|
||||
|
||||
// Features
|
||||
virtual void AddFeature(WorkerId_t id, AuSPtr<Threading::Threads::IThreadFeature> feature, bool async = false) = 0;
|
||||
|
||||
// Debug
|
||||
virtual void AssertInThreadGroup(ThreadGroup_t group) = 0;
|
||||
virtual void AssertWorker(WorkerId_t id) = 0;
|
||||
|
||||
// Async subsystem glue
|
||||
virtual bool ScheduleLoopSource(const AuSPtr<Loop::ILoopSource> &loopSource, WorkerId_t workerId, AuUInt32 timeout, const AuConsumer<AuSPtr<Loop::ILoopSource>, bool> &callback) = 0;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IWorkItem.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IWorkItem
|
||||
{
|
||||
virtual AuSPtr<IWorkItem> WaitFor(const AuSPtr<IWorkItem> &workItem) = 0;
|
||||
virtual AuSPtr<IWorkItem> WaitFor(const AuList<AuSPtr<IWorkItem>> &workItem) = 0;
|
||||
|
||||
// ms = time relative to the current time
|
||||
virtual AuSPtr<IWorkItem> SetSchedTime(AuUInt32 ms) = 0;
|
||||
|
||||
// ns = Aurora::Time::CurrentClockMS() + relativeMs
|
||||
virtual AuSPtr<IWorkItem> SetSchedTimeAbs(AuUInt32 ms) = 0;
|
||||
|
||||
// ns = time relative to the current time
|
||||
virtual AuSPtr<IWorkItem> SetSchedTimeNs(AuUInt64 ns) = 0;
|
||||
|
||||
// ns = Aurora::Time::CurrentClockNS() + relativeNs
|
||||
virtual AuSPtr<IWorkItem> SetSchedTimeNsAbs(AuUInt64 ns) = 0;
|
||||
|
||||
// ms = time relative to the time at which the work item would otherwise dispatch
|
||||
virtual AuSPtr<IWorkItem> AddDelayTime(AuUInt32 ms) = 0;
|
||||
|
||||
// ns = time relative to the time at which the work item would otherwise dispatch
|
||||
virtual AuSPtr<IWorkItem> AddDelayTimeNs(AuUInt64 ns) = 0;
|
||||
|
||||
virtual AuSPtr<IWorkItem> Then(const AuSPtr<IWorkItem> &next) = 0;
|
||||
|
||||
virtual AuSPtr<IWorkItem> Dispatch() = 0;
|
||||
|
||||
// per work frame, available work is dequed and sorted.
|
||||
// work is sorted by prio and then by initial submission index.
|
||||
//
|
||||
// prios under .25 will yield for new work at time of possible dispatch
|
||||
// only if there are work entries of > .5 in the pending work queue.
|
||||
virtual void SetPrio(float val = 0.5f) = 0;
|
||||
|
||||
virtual bool BlockUntilComplete() = 0;
|
||||
virtual bool HasFinished() = 0;
|
||||
virtual bool HasFailed() = 0;
|
||||
virtual void Cancel() = 0;
|
||||
|
||||
virtual void *GetPrivateData() = 0;
|
||||
virtual AuOptional<void *> ToWorkResultT() = 0;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IWorkItemHandler.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IWorkItemHandler
|
||||
{
|
||||
enum class EProcessNext
|
||||
{
|
||||
eInvalid = -1,
|
||||
eFinished = 0,
|
||||
eRerun,
|
||||
eSchedule,
|
||||
eFailed
|
||||
};
|
||||
|
||||
struct ProcessInfo
|
||||
{
|
||||
ProcessInfo(bool finished) : type(finished ? EProcessNext::eFinished : EProcessNext::eFailed) {}
|
||||
ProcessInfo(EProcessNext type) : type(type) {}
|
||||
ProcessInfo(const AuList<AuSPtr<IWorkItem>> &blockedBy) : type(EProcessNext::eSchedule), waitFor(blockedBy) {}
|
||||
// ...
|
||||
|
||||
EProcessNext type;
|
||||
AuList<AuSPtr<IWorkItem>> waitFor;
|
||||
AuUInt32 reschedMs;
|
||||
AuUInt64 reschedNs;
|
||||
// @hideinitializer
|
||||
IThreadPool *pool;
|
||||
};
|
||||
|
||||
virtual void DispatchFrame(ProcessInfo &info) = 0;
|
||||
|
||||
/// A really terrible name for the overloadable method that serves as the critical failure callback
|
||||
/// You have a 'shutdown'/free function you can overload, it's called the dtor
|
||||
/// Don't moan about the shitty naming of this, im not refactoring it
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void *GetPrivateData() { return nullptr; }
|
||||
};
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: JobFrom.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
template<class Info_t = AVoid, class Result_t = AVoid, class Callable_t>
|
||||
static inline FJob<Info_t, Result_t> JobFromPairConsumer(const /*AuConsumer<const Info_t &, const Result_t &> */ Callable_t&onSuccess)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(in, a);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid, class Callable_t>
|
||||
static inline FJob<Info_t, Result_t> JobFromResultConsumer(/*AuConsumer<const Result_t &> */ Callable_t&&onSuccess)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(a);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename Out_t = AVoid, typename ... Args, typename Callable_t>
|
||||
FJob<std::tuple<Args...>, Out_t> JobFromTupleConsumer(/*AuConsumer<Args..., const Result_t &> */ Callable_t &&onSuccess)
|
||||
{
|
||||
FJob<std::tuple<Args...>, Out_t> ret;
|
||||
static_assert(std::is_same_v<std::tuple<Args...>, std::tuple<AuUInt64, AuUInt64>>);
|
||||
ret.onSuccess = [=](const std::tuple<Args...> &in, const Out_t &a)
|
||||
{
|
||||
std::apply(onSuccess, std::tuple_cat(in, std::make_tuple<const Out_t &>(a)));
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename Out_t = AVoid, typename ... Args, typename Callable_t, typename FailureCallable_t>
|
||||
FJob<std::tuple<Args...>, Out_t> JobFromTupleConsumerEx(/*AuConsumer<Args..., const Result_t &> */ Callable_t &&onSuccess, FailureCallable_t &&onFailure)
|
||||
{
|
||||
FJob<std::tuple<Args...>, Out_t> ret;
|
||||
static_assert(std::is_same_v<std::tuple<Args...>, std::tuple<AuUInt64, AuUInt64>>);
|
||||
ret.onSuccess = [=](const std::tuple<Args...> &in, const Out_t &a)
|
||||
{
|
||||
std::apply(onSuccess, std::tuple_cat(in, std::make_tuple<const Out_t &>(a)));
|
||||
};
|
||||
|
||||
ret.onFailure = [=](const std::tuple<Args...> &in)
|
||||
{
|
||||
std::apply(onFailure, in);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromPairConsumerEx(const AuConsumer<const Info_t &, const Result_t &> &onSuccess, const AuConsumer<const Info_t &> &onFailure)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(in, a);
|
||||
};
|
||||
ret.onFailure = [=](const Info_t &a)
|
||||
{
|
||||
onFailure(a);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromConsumer(const AuConsumer<const Result_t &> &onSuccess, const AuVoidFunc &onFailure)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
onSuccess(a);
|
||||
};
|
||||
ret.onFailure = [=](const Info_t &a)
|
||||
{
|
||||
onFailure();
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid, class BaseInfo_t = AVoid, class BaseResult_t = AVoid>
|
||||
static inline FJob<Info_t, Result_t> JobFromDerivedJob(const FJob<BaseInfo_t, BaseResult_t> &reference)
|
||||
{
|
||||
FJob<Info_t, Result_t> ret;
|
||||
ret.onSuccess = [=](const Info_t &in, const Result_t &a)
|
||||
{
|
||||
if (reference.onSuccess)
|
||||
{
|
||||
reference.onSuccess(in, a);
|
||||
}
|
||||
};
|
||||
ret.onFailure = [=](const Info_t &a)
|
||||
{
|
||||
if (reference.onFailure)
|
||||
{
|
||||
reference.onFailure(a);
|
||||
}
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename ReturnValue_t = void, typename Arg0_t, typename ... Args>
|
||||
static inline FJob<std::tuple<Arg0_t, Args...>, ReturnValue_t> JobFromTupleClazz(const AuConsumer<const Arg0_t &, const ReturnValue_t &> &onSuccess)
|
||||
{
|
||||
FJob<std::tuple<Arg0_t, Args...>, ReturnValue_t> ret;
|
||||
ret.onSuccess = [=](const std::tuple<Arg0_t, Args...> &in, const ReturnValue_t &out)
|
||||
{
|
||||
onSuccess(std::get<0>(in), out);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename ReturnValue_t = void, typename Arg0_t, typename ... Args>
|
||||
static inline FJob<std::tuple<Arg0_t, Args...>, ReturnValue_t> JobFromTupleClazzEx(const AuConsumer<const Arg0_t &, const ReturnValue_t &> &onSuccess, const AuConsumer<const Arg0_t &> &onFailure)
|
||||
{
|
||||
FJob<std::tuple<Arg0_t, Args...>, ReturnValue_t> ret;
|
||||
ret.onSuccess = [=](const std::tuple<Arg0_t, Args...> &in, const ReturnValue_t &out)
|
||||
{
|
||||
onSuccess(std::get<0>(in), out);
|
||||
};
|
||||
|
||||
ret.onFailure = [=](const std::tuple<Arg0_t, Args...> &in)
|
||||
{
|
||||
onFailure(std::get<0>(in));
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<typename ReturnValue_t = void, typename Clazz_t, typename ... Args>
|
||||
static inline FJob<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t> JobFromTupleClazz(const AuConsumer<const ReturnValue_t &> &onSuccess)
|
||||
{
|
||||
FJob<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t> ret;
|
||||
ret.onSuccess = [=](const std::tuple<AuSPtr<Clazz_t>, Args...> &in, const ReturnValue_t &out)
|
||||
{
|
||||
onSuccess(out);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename ReturnValue_t = void, typename Clazz_t, typename ... Args>
|
||||
static inline FJob<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t> JobFromTupleClazz(const AuConsumer<Args..., const ReturnValue_t &> &onSuccess)
|
||||
{
|
||||
FJob<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t> ret;
|
||||
ret.onSuccess = [=](const std::tuple<AuSPtr<Clazz_t>, Args...> &in, const ReturnValue_t &out)
|
||||
{
|
||||
std::apply(onSuccess, std::tuple_cat(AuTuplePopFront(in), std::make_tuple<const ReturnValue_t &>(out)));
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename ReturnValue_t = void, typename ... Args>
|
||||
static inline FJob<std::tuple<Args...>, ReturnValue_t> JobFromTupleResultConsumer(const AuConsumer<const ReturnValue_t &> &onSuccess)
|
||||
{
|
||||
FJob<std::tuple<Args...>, ReturnValue_t> ret;
|
||||
ret.onSuccess = [=](const std::tuple<Args...> &in, const ReturnValue_t &out)
|
||||
{
|
||||
onSuccess(out);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename ReturnValue_t = void, typename ... Args>
|
||||
static inline FJob<std::tuple<Args...>, ReturnValue_t> JobFromTuple(const AuConsumer<Args..., const ReturnValue_t &> &onSuccess)
|
||||
{
|
||||
FJob<std::tuple<Args...>, ReturnValue_t> ret;
|
||||
ret.onSuccess = [=](const std::tuple<Args...> &in, const ReturnValue_t &out)
|
||||
{
|
||||
std::apply(onSuccess, std::tuple_cat(in, std::make_tuple<const ReturnValue_t &>(out)));
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Jobs.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct FJob
|
||||
{
|
||||
using InfoType_t = Info_t;
|
||||
using ResultType_t = Result_t;
|
||||
|
||||
AuFunction<void(const Info_t &, const Result_t &)> onSuccess;
|
||||
AuFunction<void(const Info_t &)> onFailure;
|
||||
};
|
||||
|
||||
using FVoidJob = FJob<AVoid, AVoid>;
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct CJob
|
||||
{
|
||||
using InfoType_t = Info_t;
|
||||
using ResultType_t = Result_t;
|
||||
|
||||
void(* onSuccess)(const Info_t &, const Result_t &);
|
||||
void(* onFailure)(const Info_t &);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||