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 fixes
This commit is contained in:
parent
4109852d06
commit
99c5e1fa65
192
.gitignore
vendored
192
.gitignore
vendored
@ -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
|
75
Include/Aurora/Async/AsyncTypes.hpp
Normal file
75
Include/Aurora/Async/AsyncTypes.hpp
Normal file
@ -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;
|
||||
};
|
||||
}
|
25
Include/Aurora/Async/IAsyncApp.hpp
Normal file
25
Include/Aurora/Async/IAsyncApp.hpp
Normal file
@ -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;
|
||||
};
|
||||
}
|
70
Include/Aurora/Async/IThreadPool.hpp
Normal file
70
Include/Aurora/Async/IThreadPool.hpp
Normal file
@ -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;
|
||||
};
|
||||
}
|
54
Include/Aurora/Async/IWorkItem.hpp
Normal file
54
Include/Aurora/Async/IWorkItem.hpp
Normal file
@ -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;
|
||||
};
|
||||
}
|
47
Include/Aurora/Async/IWorkItemHandler.hpp
Normal file
47
Include/Aurora/Async/IWorkItemHandler.hpp
Normal file
@ -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; }
|
||||
};
|
||||
}
|
186
Include/Aurora/Async/JobFrom.hpp
Normal file
186
Include/Aurora/Async/JobFrom.hpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
33
Include/Aurora/Async/Jobs.hpp
Normal file
33
Include/Aurora/Async/Jobs.hpp
Normal file
@ -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 &);
|
||||
};
|
||||
}
|
87
Include/Aurora/Async/TaskFrom.hpp
Normal file
87
Include/Aurora/Async/TaskFrom.hpp
Normal file
@ -0,0 +1,87 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: TaskFrom.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
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 Out_t = AVoid, typename ... Args, typename Functor>
|
||||
FTask<std::tuple<Args...>, Out_t> TaskFromTupleCallable(Functor &&func)
|
||||
{
|
||||
FTask<std::tuple<Args...>, Out_t> ret;
|
||||
static_assert(std::is_same_v<std::tuple<Args...>, std::tuple<AuUInt64, AuUInt64>>);
|
||||
ret.onFrame = [callable = func](const std::tuple<Args...> &in) -> Out_t
|
||||
{
|
||||
return std::apply(callable, in);
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename Out_t = AVoid, typename Owner_t, typename ... Args>
|
||||
FTask<std::tuple<Args...>, Out_t> TaskFromTupleCallableWithOwnerArg(AuFunction<Out_t(Args...)> &&func, const Owner_t &ownerToPin)
|
||||
{
|
||||
FTask<std::tuple<Owner_t, Args...>, Out_t> ret;
|
||||
ret.onFrame = [callable = func](const std::tuple<Args...> &in) -> Out_t
|
||||
{
|
||||
return std::apply(callable, std::tuple_cat(std::make_tuple<Owner_t>(ownerToPin), in));
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename Task_t, typename ReturnValue_t, typename ... Args, typename Functor>
|
||||
Task_t TaskFromTupleCallableWithBindOwner(Functor &&func)
|
||||
{
|
||||
Task_t ret;
|
||||
ret.onFrame = [callable = func](const auto &in) -> ReturnValue_t
|
||||
{
|
||||
return std::apply(callable, AuTuplePopFront(in));
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename Task_t, typename ReturnValue_t, typename Functor>
|
||||
Task_t TaskFromTupleCallableWithBindOwner2(Functor func)
|
||||
{
|
||||
Task_t ret;
|
||||
ret.onFrame = [callable = func](const auto &in) -> ReturnValue_t
|
||||
{
|
||||
return std::apply(callable, AuTuplePopFront(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;
|
||||
}
|
||||
}
|
32
Include/Aurora/Async/Tasks.hpp
Normal file
32
Include/Aurora/Async/Tasks.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Tasks.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct FTask
|
||||
{
|
||||
using InfoType_t = Info_t;
|
||||
using ResultType_t = Result_t;
|
||||
|
||||
AuFunction<Result_t(const Info_t &)> onFrame;
|
||||
};
|
||||
|
||||
|
||||
template<class Info_t = AVoid, class Result_t = AVoid>
|
||||
struct CTask
|
||||
{
|
||||
using InfoType_t = Info_t;
|
||||
using ResultType_t = Result_t;
|
||||
|
||||
Result_t(* onFrame)(const Info_t &) = 0;
|
||||
};
|
||||
|
||||
using FVoidTask = FTask<AVoid, AVoid>;
|
||||
}
|
118
Include/Aurora/Async/WorkBasic.hpp
Normal file
118
Include/Aurora/Async/WorkBasic.hpp
Normal file
@ -0,0 +1,118 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: WorkBasic.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
|
||||
/// --- THREAD POOL --
|
||||
|
||||
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> NewWork(const WorkerPId_t &worker, const Task_t &task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return worker.pool->NewWorkItem(worker, AuMakeShared<WorkPairImpl<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> DispatchWork(const WorkerPId_t &worker, const Task_t &task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkPairImpl<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> NewWork(const WorkerPId_t &worker, Task_t &&task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return worker.pool->NewWorkItem(worker, AuMakeShared<WorkPairImpl<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> DispatchWork(const WorkerPId_t &worker, Task_t &&task, const Job_t &job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkPairImpl<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> NewWork(const WorkerPId_t &worker, Task_t &&task, Job_t &&job, bool enableWait = false)
|
||||
{
|
||||
return worker.pool->NewWorkItem(worker, AuMakeShared<WorkPairImpl<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> DispatchWork(const WorkerPId_t &worker, Task_t &&task, Job_t &&job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkPairImpl<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> DispatchWork(const WorkerPId_t &worker, const Task_t &task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return worker.pool->NewWorkItem(worker, AuMakeShared<WorkPairImpl<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 WorkerPId_t &worker, ClazzImpl task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return DispatchWorkPairImpl<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 WorkerPId_t &worker, ClazzImpl task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return DispatchWorkPairImpl<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 WorkerPId_t &worker, ClazzImpl task, const Job_t &job, const Info_t &inputParameters, bool enableWait = false)
|
||||
{
|
||||
return DispatchWorkPairImpl<Info_t, Result_t, Task_t, Job_t>(worker, TaskFromVoidVoid<Info_t, Result_t>(task), job, inputParameters, enableWait);
|
||||
}
|
||||
|
||||
/// --- ASYNC APP --
|
||||
|
||||
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>, class a = const Job_t &, class b = Task_t &>
|
||||
static AuSPtr<IWorkItem> NewWork(const WorkerId_t &worker, a task, b job, bool enableWait = false)
|
||||
{
|
||||
return NewWorkItem(worker, AuMakeShared<WorkPairImpl<Info_t, Result_t, Task_t>>(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>, class a = const Job_t &, class b = Task_t &>
|
||||
static AuSPtr<IWorkItem> DispatchWork(const WorkerId_t &worker, a task, b job, bool enableWait = false)
|
||||
{
|
||||
return NewWork<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>, class a, class b, class c>
|
||||
static AuSPtr<IWorkItem> NewWork(const WorkerId_t &worker, a task, b job, c info, bool enableWait = false)
|
||||
{
|
||||
return NewWorkItem(worker, AuMakeShared<WorkPairImpl<Info_t, Result_t, Task_t>>(task, job, info), 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>, class a, class b, class c>
|
||||
static AuSPtr<IWorkItem> DispatchWork(const WorkerId_t &worker, a task, b job, c info, bool enableWait = false)
|
||||
{
|
||||
return NewWork<Info_t, Result_t, Task_t, Job_t>(worker, task, job, info, 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, class a = const Job_t &, class b = const Info_t &>
|
||||
AuSPtr<IWorkItem> DispatchFunctional(const WorkerId_t &worker, ClazzImpl task, a job, b inputParameters, bool enableWait = false)
|
||||
{
|
||||
return NewWork<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, class a = const Job_t &, class b = const Info_t &>
|
||||
AuSPtr<IWorkItem> DispatchFunctor(const WorkerId_t &worker, ClazzImpl task, a job, b inputParameters, bool enableWait = false)
|
||||
{
|
||||
return NewWork<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, class a = const Job_t &, class b = const Info_t &>
|
||||
AuSPtr<IWorkItem> DispatchVoid(const WorkerId_t &worker, ClazzImpl task, a job, b inputParameters, bool enableWait = false)
|
||||
{
|
||||
return NewWork<Info_t, Result_t, Task_t, Job_t>(worker, TaskFromVoidVoid<Info_t, Result_t>(task), job, inputParameters, enableWait);
|
||||
}
|
||||
}
|
372
Include/Aurora/Async/WorkPairImpl.hpp
Normal file
372
Include/Aurora/Async/WorkPairImpl.hpp
Normal file
@ -0,0 +1,372 @@
|
||||
/***
|
||||
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;
|
||||
};
|
||||
|
||||
#if 0
|
||||
/// @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 WorkPairImpl : IWorkItemHandler, std::enable_shared_from_this<IWorkItemHandler>
|
||||
{
|
||||
|
||||
Info_t input;
|
||||
Task_t task;
|
||||
Job_t callback;
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
WorkerPId_t caller_;
|
||||
|
||||
BasicWorkCtx secretContext_;
|
||||
Result_t resultValue_;
|
||||
Threading::Primitives::SpinLock lock_;
|
||||
|
||||
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());
|
||||
|
||||
AuFunction<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_ == Async::GetCurrentWorkerPId())
|
||||
{
|
||||
func();
|
||||
}
|
||||
else
|
||||
{
|
||||
AuFunction<void()> err = [pin]()
|
||||
{
|
||||
pin->CallOnFailure();
|
||||
};
|
||||
|
||||
// TODO: this is somewhat evil. double alloc when we could reuse this
|
||||
if (!caller_.pool->NewWorkItem(caller_, AuMakeShared<BasicWorkStdFunc>(func, err))->Dispatch())
|
||||
{
|
||||
pin->CallOnFailure();
|
||||
}
|
||||
}
|
||||
|
||||
caller_ = {};
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() override
|
||||
{
|
||||
AU_LOCK_GUARD(this->lock_);
|
||||
ShutdownNoLock();
|
||||
}
|
||||
|
||||
inline void ShutdownNoLock()
|
||||
{
|
||||
caller_ = {};
|
||||
try
|
||||
{
|
||||
CallOnFailure();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
}
|
||||
}
|
||||
|
||||
inline void CallOnFailure()
|
||||
{
|
||||
if constexpr (IsCallbackPtr)
|
||||
{
|
||||
if constexpr (AuIsBaseOfTemplate<AuFunction, decltype(callback->onFailure)>::value)
|
||||
{
|
||||
if (!callback->onFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback->onFailure(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AuIsBaseOfTemplate<AuFunction, decltype(callback.onFailure)>::value)
|
||||
{
|
||||
if (!callback.onFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback.onFailure(input);
|
||||
}
|
||||
}
|
||||
};
|
||||
#else
|
||||
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 WorkPairImpl : IWorkItemHandler, std::enable_shared_from_this<IWorkItemHandler>
|
||||
{
|
||||
WorkPairImpl() : caller_(Async::GetCurrentWorkerPId())
|
||||
{}
|
||||
|
||||
WorkPairImpl(Task_t &&task) : task(std::move(task)), caller_(Async::GetCurrentWorkerPId())
|
||||
{}
|
||||
|
||||
WorkPairImpl(Task_t &&task, Job_t &&callback) : task(std::move(task)), callback(std::move(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(std::move(task)), callback(callback), input(info), caller_(Async::GetCurrentWorkerPId())
|
||||
{}
|
||||
|
||||
WorkPairImpl(Task_t &&task, Job_t &&callback, const Info_t &info) : task(std::move(task)), callback(std::move(callback)), input(info), caller_(Async::GetCurrentWorkerPId())
|
||||
{}
|
||||
|
||||
WorkPairImpl(Task_t &&task, Job_t &&callback, Info_t &&info) : task(std::move(task)), callback(std::move(callback)), input(std::move(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<Info_t, Result_t, Task_t, Job_t> &SetTask(const Task_t &task)
|
||||
{
|
||||
this->task = task;
|
||||
return *this;
|
||||
}
|
||||
|
||||
WorkPairImpl<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;
|
||||
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)
|
||||
{
|
||||
resultValue_ = task->onFrame(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultValue_ = task.onFrame(input);
|
||||
}
|
||||
|
||||
task = {};
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
Shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
auto pin = AuSharedFromThis();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
pin->callback = {};
|
||||
pin->caller_ = {};
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
if (caller_ == Async::GetCurrentWorkerPId())
|
||||
{
|
||||
func();
|
||||
}
|
||||
else
|
||||
{
|
||||
AuFunction<void()> err = [pin]()
|
||||
{
|
||||
pin->CallOnFailure();
|
||||
};
|
||||
|
||||
// TODO: this is somewhat evil. double alloc when we could reuse this
|
||||
if (!caller_.pool->NewWorkItem(caller_, AuMakeShared<BasicWorkStdFunc>(func, err))->Dispatch())
|
||||
{
|
||||
pin->CallOnFailure();
|
||||
}
|
||||
}
|
||||
|
||||
caller_ = {};
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
}
|
@ -12,10 +12,10 @@
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct WorkerId_t;
|
||||
struct WorkerPId_t;
|
||||
}
|
||||
|
||||
#include "ICommandSubscriber.hpp"
|
||||
#include "ITextLineSubscriber.hpp"
|
||||
|
||||
namespace Aurora::Console::Commands
|
||||
{
|
||||
@ -30,20 +30,15 @@ namespace Aurora::Console::Commands
|
||||
* Parses `string` and dispatches the parsed command on the current thread instantly
|
||||
*/
|
||||
AUKN_SYM bool DispatchCommandThisThread(const AuString &string);
|
||||
|
||||
|
||||
/**
|
||||
* Parses `string` on the current thread and then schedules the ICommandSubscriber callback on the specified thread
|
||||
*/
|
||||
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerId_t id);
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a processed stdin line given `string`
|
||||
* Parses `string` on the current thread and then schedules the ICommandSubscriber callback on the specified thread
|
||||
*/
|
||||
AUKN_SYM bool DispatchRawLine(const AuString &string);
|
||||
|
||||
/**
|
||||
* Hijacks the UTF-8 input line processor coming from the consoles. DispatchCommand remains unaffected
|
||||
* Call with an empty interface pointer to reenable command processing
|
||||
*/
|
||||
AUKN_SYM void SetCallbackAndDisableCmdProcessing(const AuSPtr<ITextLineSubscriber> &subscriber);
|
||||
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerPId_t id);
|
||||
}
|
@ -16,16 +16,17 @@ namespace Aurora::Console
|
||||
AUKN_SYM void WriteLine(const ConsoleMessage &msg);
|
||||
|
||||
/// Consider using the following function for asynchronous utf-8 processed line based input -
|
||||
/// Aurora::Console::Commands::SetCallbackAndDisableCmdProcessing(...)
|
||||
/// [!!!] nonblocking
|
||||
/// [!!!] you must disable the stdio console logger before using this api
|
||||
/// Hooks::SetCallbackAndDisableCmdProcessing(...)
|
||||
AUKN_SYM AuUInt32 ReadStdIn(void *buffer, AuUInt32 length);
|
||||
|
||||
/// [!!!] nonblocking
|
||||
/// [!!!] you should disable the stdio console logger before using this api
|
||||
/// [!!!] expect system locale if the logger is not enabled
|
||||
AUKN_SYM AuUInt32 WriteStdIn(const void *buffer, AuUInt32 length);
|
||||
/// Consider using AuLog for general purpose use
|
||||
AUKN_SYM AuUInt32 WriteStdOut(const void *buffer, AuUInt32 length);
|
||||
|
||||
/**
|
||||
* Simulates a processed stdin line given a UTF8 string
|
||||
*/
|
||||
AUKN_SYM bool DispatchRawLine(const AuString &string);
|
||||
|
||||
AUKN_SYM void OpenLateStd();
|
||||
AUKN_SYM void OpenLateGUI();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IConsoleSubscriber.hpp"
|
||||
#include "ITextLineSubscriber.hpp"
|
||||
|
||||
namespace Aurora::Console::Hooks
|
||||
{
|
||||
@ -19,4 +20,13 @@ namespace Aurora::Console::Hooks
|
||||
/// @deprecated wont ever remove
|
||||
/// Most c++ styleguides would have you chuck an IConsoleSubscriber in your app somewhere
|
||||
AUKN_SYM void AddFunctionalHook(LineHook_cb hook);
|
||||
|
||||
|
||||
/**
|
||||
* Hijacks the UTF-8 input line processor coming from the consoles.
|
||||
* Call with an empty interface pointer to reenable command processing
|
||||
*
|
||||
* Requires AsyncApp or RuntimePump() calls
|
||||
*/
|
||||
AUKN_SYM void SetCallbackAndDisableCmdProcessing(const AuSPtr<Hooks::ITextLineSubscriber> &subscriber);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Console::Commands
|
||||
namespace Aurora::Console::Hooks
|
||||
{
|
||||
AUKN_INTERFACE(ITextLineSubscriber,
|
||||
AUI_METHOD(void, OnProcessedLineUTF8, (const AuString &, line))
|
17
Include/Aurora/Console/Logging/IBasicSink.hpp
Normal file
17
Include/Aurora/Console/Logging/IBasicSink.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IBasicSink.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Console::Logging
|
||||
{
|
||||
AUKN_INTERFACE(IBasicSink,
|
||||
AUI_METHOD(void, OnMessageBlocking, (const ConsoleMessage &, msg)),
|
||||
AUI_METHOD(void, OnMessageNonblocking, (const ConsoleMessage &, msg)),
|
||||
AUI_METHOD(void, OnFlush, ())
|
||||
)
|
||||
}
|
17
Include/Aurora/Console/Logging/IBasicSinkRB.hpp
Normal file
17
Include/Aurora/Console/Logging/IBasicSinkRB.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IBasicSinkRB.hpp
|
||||
Date: 2021-11-2
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Console::Logging
|
||||
{
|
||||
struct IBasicSinkRB : IBasicSink
|
||||
{
|
||||
virtual void SaveToPath(const AuString &path, bool plainText = false) = 0;
|
||||
virtual AuList<ConsoleMessage> Export() = 0;
|
||||
};
|
||||
}
|
84
Include/Aurora/Console/Logging/ILogger.hpp
Normal file
84
Include/Aurora/Console/Logging/ILogger.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: ILogger.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Console::Logging
|
||||
{
|
||||
struct ILogger
|
||||
{
|
||||
virtual void WriteMessage(const ConsoleMessage &msg) = 0;
|
||||
|
||||
#if defined(_AUHAS_FMT)
|
||||
template<typename ... T>
|
||||
inline void WriteLinef(const AuString &tag, const AuString &msg, T&& ... args)
|
||||
{
|
||||
WriteMessage(ConsoleMessage(EAnsiColor::eReset, tag, fmt::format(msg, std::forward<T>(args)...)));
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void WriteLinef(EAnsiColor color, const AuString &tag, const AuString &msg, T&& ... args)
|
||||
{
|
||||
WriteMessage(ConsoleMessage(color, tag, fmt::format(msg, std::forward<T>(args)...)));
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void LogVerbose(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eYellow, "Verbose", line, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
#if defined(STAGING) || defined(DEBUG)
|
||||
template<typename ... T>
|
||||
inline void LogVerboseNoShip(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eYellow, "Verbose", line, std::forward<T>(args)...);
|
||||
}
|
||||
#else
|
||||
template<typename ... T>
|
||||
inline void LogVerboseNoShip(const AuString &line, T&& ... args)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void DoNothing()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void LogInfo(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eGreen, "Info", line, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void LogDbg(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eYellow, "Debug", line, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void LogWarn(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eRed, "Warn", line, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void LogError(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eBoldRed, "Error", line, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
inline void LogGame(const AuString &line, T&& ... args)
|
||||
{
|
||||
WriteLinef(EAnsiColor::eBlue, "Game", line, std::forward<T>(args)...);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
@ -2,11 +2,16 @@
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Logging.hpp
|
||||
Date: 2021-6-9
|
||||
Date: 2021-9-21
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "ILogger.hpp"
|
||||
#include "IBasicSink.hpp"
|
||||
#include "IBasicSinkRB.hpp"
|
||||
#include "Sinks.hpp"
|
||||
|
||||
namespace Aurora::Console::Logging
|
||||
{
|
||||
#if defined(_AUHAS_FMT)
|
||||
@ -35,9 +40,14 @@ namespace Aurora::Console::Logging
|
||||
WriteLinef(EAnsiColor::eYellow, "Verbose", line, std::forward<T>(args)...);
|
||||
}
|
||||
#else
|
||||
#define LogVerboseNoShip(...)
|
||||
#define LogVerboseNoShip(...) DoNothing()
|
||||
#endif
|
||||
|
||||
static inline void DoNothing()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
static inline void LogInfo(const AuString &line, T&& ... args)
|
||||
{
|
||||
|
22
Include/Aurora/Console/Logging/Sinks.hpp
Normal file
22
Include/Aurora/Console/Logging/Sinks.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Sinks.hpp
|
||||
Date: 2021-11-2
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora
|
||||
{
|
||||
struct SocketConsole;
|
||||
}
|
||||
|
||||
namespace Aurora::Console::Logging
|
||||
{
|
||||
AUKN_SHARED_API(NewGlobalPipeSink, IBasicSink);
|
||||
AUKN_SHARED_API(NewFileSink, IBasicSink, const AuString &path, bool binary = false);
|
||||
AUKN_SHARED_API(NewIPCSink, IBasicSink, const SocketConsole &console);
|
||||
AUKN_SHARED_API(NewRingLogger, IBasicSinkRB, AuUInt32 approxMaxBytes);
|
||||
AUKN_SHARED_API(NewLogger, ILogger, const AuList<AuSPtr<IBasicSink>> &sinks);
|
||||
}
|
15
Include/Aurora/Console/README.md
Normal file
15
Include/Aurora/Console/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Features
|
||||
|
||||
* Simple global logger
|
||||
* UTF8 support
|
||||
* Colorful
|
||||
* WxWidgets GUI for Windowed applications
|
||||
* Async process line in subscription interface
|
||||
* Async logger write message out subscription interface
|
||||
* Optional command processing using the parse subsystem
|
||||
** Commands may be dispatched on specific threads
|
||||
* Supports various backends
|
||||
** A log file keeping under branded user app directory OR the cwd
|
||||
** Windows debugger `OutputDebugString()`
|
||||
** Stdout respecting system locale and color support
|
||||
** syslog
|
@ -54,7 +54,7 @@ namespace Aurora::Data
|
||||
case EDataType::kTypeStructInt32: return 4;
|
||||
case EDataType::kTypeStructUInt64: return 8;
|
||||
case EDataType::kTypeStructInt64: return 8;
|
||||
default: return 0;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2,210 +2,345 @@
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Net.hpp
|
||||
Date: 2021-6-9
|
||||
Date: 2021-9-21
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IThreadPool;
|
||||
}
|
||||
|
||||
namespace Aurora::IO::Net
|
||||
{
|
||||
enum TransportProtocol
|
||||
{
|
||||
kProtocolUDP,
|
||||
kProtocolTCP
|
||||
};
|
||||
static const AuUInt16 kMagicPortAny = 65535;
|
||||
|
||||
enum IPProtocol
|
||||
{
|
||||
kIPProtocolV4,
|
||||
kIPProtocolV6
|
||||
};
|
||||
struct INetworkStream;
|
||||
struct IBasicSocket;
|
||||
struct IClientSocket;
|
||||
struct IServer;
|
||||
|
||||
AUE_DEFINE(ETransportProtocol, (
|
||||
eProtocolInvalid,
|
||||
eProtocolUDP,
|
||||
eProtocolTCP
|
||||
));
|
||||
|
||||
AUE_DEFINE(EIPProtocol, (
|
||||
eIPProtocolInvalid,
|
||||
eIPProtocolV4,
|
||||
eIPProtocolV6
|
||||
));
|
||||
|
||||
struct IPAddress
|
||||
{
|
||||
IPProtocol ip;
|
||||
bool hasIP;
|
||||
AuString address;
|
||||
EIPProtocol ip;
|
||||
union
|
||||
{
|
||||
AuUInt8 v4[4];
|
||||
AuUInt16 v6[8];
|
||||
};
|
||||
|
||||
AUKN_SYM IPAddress(const AuString &parse);
|
||||
|
||||
AUKN_SYM AuString ToString();
|
||||
AUKN_SYM bool IsValid();
|
||||
|
||||
AUKN_SYM AuString toString();
|
||||
inline bool operator ==(const IPAddress &cmp) const
|
||||
{
|
||||
if (cmp.ip != this->ip) return false;
|
||||
|
||||
if (cmp.ip == EIPProtocol::eIPProtocolV4)
|
||||
{
|
||||
return memcmp(cmp.v4, this->v4, sizeof(this->v4)) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return memcmp(cmp.v6, this->v6, sizeof(this->v6)) == 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Endpoint
|
||||
{
|
||||
TransportProtocol protocol;
|
||||
IPAddress address;
|
||||
AuUInt16 port;
|
||||
AUE_DEFINE(ESocketInfo, (
|
||||
eByDns,
|
||||
eByEndpoint
|
||||
));
|
||||
|
||||
struct SocketHostName
|
||||
{
|
||||
SocketHostName(const AuString &name) : info(ESocketInfo::eByDns), hostname(name), address({})
|
||||
{}
|
||||
|
||||
SocketHostName(const IPAddress &endpoint) : info(ESocketInfo::eByEndpoint), address(endpoint), hostname()
|
||||
{}
|
||||
|
||||
const ESocketInfo info;
|
||||
const AuString hostname;
|
||||
const IPAddress address;
|
||||
|
||||
inline bool operator ==(const SocketHostName &cmp) const
|
||||
{
|
||||
if (cmp.info != this->info) return false;
|
||||
if (cmp.info == ESocketInfo::eByEndpoint)
|
||||
{
|
||||
return cmp.address == this->address;
|
||||
}
|
||||
else
|
||||
{
|
||||
return cmp.hostname == this->hostname;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct IPEndpoint
|
||||
{
|
||||
IPAddress ip;
|
||||
AuUInt16 port;
|
||||
};
|
||||
|
||||
struct ConnectionEndpoint
|
||||
{
|
||||
ETransportProtocol protocol;
|
||||
bool tls {};
|
||||
bool compressed {};
|
||||
// 0 - destination is a stateless datagram server
|
||||
// 1 - destination is a psuedo-stateful server
|
||||
AuUInt UDPTimeout;
|
||||
|
||||
AUKN_SYM AuString toString();
|
||||
AuUInt32 UDPTimeoutInMS;
|
||||
};
|
||||
|
||||
class INetworkStream;
|
||||
class IBasicSocket;
|
||||
class IClientSocket;
|
||||
struct ITLSHandshakeAuthenticate
|
||||
{
|
||||
virtual AuSPtr<Crypto::X509::DecodedCertificate> GetCertificate() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* DTLS/UDP/TCP/TLS -> TRUE = expects another datagram or read pump
|
||||
* -> FALSE = end of socket life
|
||||
*/
|
||||
using StreamHasData_cb = std::function<bool(const AuSPtr<IClientSocket> &socket)>;
|
||||
|
||||
using InitializeSocket_cb = std::function<bool(const AuSPtr<IClientSocket> &socket)>;
|
||||
using ShutdownSocket_cb = std::function<void(const AuSPtr<IClientSocket> &socket)>;
|
||||
AUE_DEFINE(EHandleErrorClass, (
|
||||
ePinError,
|
||||
eUserDeny,
|
||||
eBrokenPacket,
|
||||
eInvalidCipher,
|
||||
eBadCert
|
||||
));
|
||||
|
||||
class IClientHasData
|
||||
struct TLSHandshakeError
|
||||
{
|
||||
public:
|
||||
// DTLS/UDP/TCP/TLS -> TRUE = expects another datagram or read pump
|
||||
// FALSE = end of socket life
|
||||
virtual bool OnSocketHasData(const AuSPtr<IClientSocket> &socket) = 0;
|
||||
EHandleErrorClass error;
|
||||
AuSPtr<ITLSHandshakeAuthenticate> session;
|
||||
AuString message;
|
||||
};
|
||||
|
||||
class FunctionalClientHasData : public IClientHasData
|
||||
{
|
||||
private:
|
||||
StreamHasData_cb socket_;
|
||||
public:
|
||||
FunctionalClientHasData(const StreamHasData_cb &socket) : socket_(socket)
|
||||
{}
|
||||
FunctionalClientHasData(StreamHasData_cb &&socket) : socket_(std::move(socket))
|
||||
{}
|
||||
};
|
||||
|
||||
class IClientShutdown
|
||||
{
|
||||
public:
|
||||
virtual void OnSocketShutdown(const AuSPtr<IClientSocket> &socket) = 0;
|
||||
};
|
||||
AUKN_INTERFACE(IClientSubscriber,
|
||||
//
|
||||
AUI_METHOD(void, OnServerConnectSuccess, (const AuSPtr<IClientSocket> &, socket)),
|
||||
AUI_METHOD(void, OnServerConnectFailed, (const AuSPtr<IClientSocket> &, socket)),
|
||||
|
||||
// DTLS/UDP/TCP/TLS -> TRUE = expects another datagram or read pump
|
||||
// FALSE = end of socket life
|
||||
AUI_METHOD(bool, OnSockeData, (const AuSPtr<IClientSocket> &, socket)),
|
||||
|
||||
class FunctionalClientShutdown : public IClientShutdown
|
||||
{
|
||||
private:
|
||||
ShutdownSocket_cb socket_;
|
||||
public:
|
||||
FunctionalClientShutdown(const ShutdownSocket_cb &socket) : socket_(socket)
|
||||
{}
|
||||
FunctionalClientShutdown(ShutdownSocket_cb &&socket) : socket_(std::move(socket))
|
||||
{}
|
||||
};
|
||||
|
||||
class IClientDoS
|
||||
{
|
||||
public:
|
||||
virtual void OnSocketDoS(const AuSPtr<IClientSocket> &socket) = 0;
|
||||
};
|
||||
//
|
||||
AUI_METHOD(void, OnSocketError, (const AuSPtr<IClientSocket> &, socket)),
|
||||
|
||||
class FunctionalClientDoS: public IClientDoS
|
||||
{
|
||||
private:
|
||||
ShutdownSocket_cb socket_;
|
||||
public:
|
||||
FunctionalClientDoS(const ShutdownSocket_cb &socket) : socket_(socket)
|
||||
{}
|
||||
FunctionalClientDoS(ShutdownSocket_cb &&socket) : socket_(std::move(socket))
|
||||
{}
|
||||
};
|
||||
|
||||
class IClientTLSBreakdown
|
||||
{
|
||||
public:
|
||||
virtual void OnSocketTLSBreakdown(const AuSPtr<IClientSocket> &socket) = 0;
|
||||
};
|
||||
|
||||
class FunctionalClientTLSBreakdown : public IClientTLSBreakdown
|
||||
{
|
||||
private:
|
||||
ShutdownSocket_cb socket_;
|
||||
public:
|
||||
FunctionalClientTLSBreakdown(const ShutdownSocket_cb &socket) : socket_(socket)
|
||||
{}
|
||||
FunctionalClientTLSBreakdown(ShutdownSocket_cb &&socket) : socket_(std::move(socket))
|
||||
{}
|
||||
};
|
||||
|
||||
class IClientEvents
|
||||
{
|
||||
public:
|
||||
virtual void AddSubscriptionHasData(const AuSPtr<IClientHasData> &iface) = 0;
|
||||
virtual void RemoveSubscriptionHasData(const AuSPtr<IClientHasData> &iface) = 0;
|
||||
//
|
||||
AUI_METHOD(void, OnSocketShutdown, (const AuSPtr<IClientSocket> &, socket))
|
||||
);
|
||||
|
||||
virtual void AddSubscriptionShutdown(const AuSPtr<IClientShutdown> &iface) = 0;
|
||||
virtual void RemoveSubscriptionShutdown(const AuSPtr<IClientShutdown> &iface) = 0;
|
||||
AUKN_INTERFACE(IClientSubscriberTls,
|
||||
AUI_METHOD(bool, OnVerifySocketCertificate, (const AuSPtr<IBasicSocket> &, socket, const AuSPtr<ITLSHandshakeAuthenticate>, session)),
|
||||
AUI_METHOD(bool, OnTLSHandleError, (const AuSPtr<IBasicSocket> &, socket, const TLSHandshakeError &, error))
|
||||
);
|
||||
|
||||
virtual void AddSubscriptionDoS(const AuSPtr<IClientDoS> &iface) = 0;
|
||||
virtual void RemoveSubscriptionDoS(const AuSPtr<IClientDoS> &iface) = 0;
|
||||
|
||||
virtual void AddSubscriptionTlsBreakdown(const AuSPtr<IClientTLSBreakdown> &iface) = 0;
|
||||
virtual void RemoveSubscriptionTlsBreakdown(const AuSPtr<IClientTLSBreakdown> &iface) = 0;
|
||||
};
|
||||
AUKN_INTERFACE(IServerSubscriber,
|
||||
AUI_METHOD(bool, OnClientAccept, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
|
||||
AUI_METHOD(bool, OnClientDoS, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
|
||||
AUI_METHOD(void, OnClientError, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
|
||||
AUI_METHOD(void, OnClientShutdown, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
|
||||
AUI_METHOD(bool, OnReadFrame, (const AuSPtr<IServer> &, server, const AuList<AuSPtr<IClientSocket>> &, sockets)),
|
||||
AUI_METHOD(void, OnShutdown, (const AuSPtr<IServer> &, server))
|
||||
);
|
||||
|
||||
AUKN_INTERFACE(IServerSubscriberTls,
|
||||
AUI_METHOD(bool, OnClientTLSReport, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket, const TLSHandshakeError &, error))
|
||||
);
|
||||
|
||||
// TODO: We should introduce another std:: customer overloadable type reproducing hardcoded ascii and an int, basically std::error_code
|
||||
// Maybe AuErrorCode = [std::, my_fav_stl::]error_code
|
||||
// AuError = something more flexable
|
||||
using Error_t = std::error_code;
|
||||
|
||||
class IBasicSocket
|
||||
struct IBasicSocketPrivateContext
|
||||
{
|
||||
public:
|
||||
virtual bool IsActive() = 0;
|
||||
virtual Error_t GetLastError() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual bool GetLocalEndpoint(Endpoint &out) = 0;
|
||||
// force vtbl
|
||||
virtual ~IBasicSocketPrivateContext()
|
||||
{}
|
||||
};
|
||||
|
||||
struct SocketStatStream
|
||||
{
|
||||
AuUInt64 total;
|
||||
AuUInt64 averageBytesPerSecond; // interpolated, behind 1 frame
|
||||
AuUInt64 extrapolatedBytesPerSecond; // this uses an extrapolated time point to predict the network bandwidth of one whole second of data
|
||||
};
|
||||
|
||||
class IClientSocket : public IBasicSocket
|
||||
struct SocketStat
|
||||
{
|
||||
public:
|
||||
|
||||
virtual IClientEvents *GetClientEvents() = 0;
|
||||
AuUInt32 timeStartMs;
|
||||
AuUInt32 uptimeMs;
|
||||
AuUInt64 bandwidthCost;
|
||||
|
||||
virtual bool Initialize() = 0;
|
||||
SocketStatStream rx;
|
||||
SocketStatStream rt;
|
||||
};
|
||||
|
||||
virtual bool GetRemoteEndpoint(Endpoint &out) = 0;
|
||||
enum class EUnderlyingModel
|
||||
{
|
||||
eAsync,
|
||||
eBlocking
|
||||
};
|
||||
|
||||
struct IBasicSocket
|
||||
{
|
||||
virtual bool IsActive() = 0;
|
||||
virtual Error_t GetLastError() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual AuSPtr<IBasicSocketPrivateContext> SetContext(const AuSPtr<IBasicSocketPrivateContext> &newContext) = 0;
|
||||
virtual AuSPtr<IBasicSocketPrivateContext> GetContext() = 0;
|
||||
virtual bool GetLocalEndpoint(ConnectionEndpoint &out) = 0;
|
||||
virtual SocketStat GetStats() = 0;
|
||||
virtual EUnderlyingModel GetThreadModel() = 0;
|
||||
};
|
||||
|
||||
AUKN_INTERFACE(ISocketSubmissionComplete,
|
||||
AUI_METHOD(void, OnWriteFinished, (AuUInt, fence))
|
||||
);
|
||||
|
||||
struct StreamConfig
|
||||
{
|
||||
bool enableBufferedInput {true};
|
||||
bool enableBufferedOutput {false};
|
||||
|
||||
// 30000 * (4096 * 10) / 1024 / 1024 / 1024
|
||||
// = 1.1GB per ~1/2 of 2^16
|
||||
// Plenty of overhead for cpu blts to go brr without becoming
|
||||
// cpu bound. If you're expecting 30k connections, you can
|
||||
// lose 1GB to our extended seak interface by default
|
||||
AuUInt32 bufferedReadSize {4096 * 10}; // see: enableBufferedInput
|
||||
AuUInt32 bufferedWriteSize {4096 * 10}; // see: enableBufferedOutput
|
||||
};
|
||||
|
||||
|
||||
struct IClientSocket : public IBasicSocket
|
||||
{
|
||||
virtual bool GetRemoteEndpoint(ConnectionEndpoint &out) = 0;
|
||||
virtual bool GetLocalEndpoint(ConnectionEndpoint &out) = 0;
|
||||
|
||||
virtual bool PumpRead() = 0;
|
||||
virtual bool PumpWrite() = 0;
|
||||
virtual bool Pump() = 0;
|
||||
|
||||
virtual void Run(int idx, AuUInt32 timeout) = 0;
|
||||
|
||||
|
||||
virtual bool ReadAsync(AuUInt8 *buffer, AuUInt32 &length, bool all = false) = 0;
|
||||
virtual bool PeakAsync(AuUInt8 *buffer, AuUInt32 &length) = 0;
|
||||
virtual bool ReadSync(AuUInt8 *buffer, AuUInt32 &length, bool all = true) = 0;
|
||||
// If memory.ptr is a nullptr, this method immediately returns with the expected write length in memory.out
|
||||
//
|
||||
// If all is true and the internal buffer is not saturated enough yet, no data is read and
|
||||
// zero readable bytes are returned.
|
||||
//
|
||||
// If all is false, copies memory.length into memory.ptr, up to memory.length
|
||||
//
|
||||
// psuedocode:
|
||||
// If all is false,
|
||||
// memory.outVariable = readableBytes
|
||||
// if memory.ptr,
|
||||
// begin copy from the internal upto max(memory.length, outVariable) into memory.ptr,
|
||||
// end
|
||||
// else
|
||||
// return readExactly(memory)
|
||||
//
|
||||
// NOTE: BufferInputStreamAdhoc usage applys to async reads as well
|
||||
virtual bool ReadAsync(const Memory::MemoryViewStreamWrite &memory, bool all = false) = 0;
|
||||
|
||||
// Atomic
|
||||
// ReadAsync(memory, false)
|
||||
// SeekAsync(-memory.outVariable)
|
||||
virtual bool PeakAsync(const Memory::MemoryViewStreamWrite &memory) = 0;
|
||||
|
||||
// Attempts to seek backwards or forwards in the UDP or TCP packet
|
||||
// If you are under the callstack of a HasXXXHasData callback, you are guaranteed (bufferedReadSize - streamPosition - streamRemaining) bytes backwards
|
||||
//
|
||||
virtual bool SeekAsync(int signedDistanceFromCur = 0);
|
||||
|
||||
virtual bool WriteAsync(const AuUInt8 *buffer, AuUInt32 length) = 0;
|
||||
virtual bool WriteAsync(const AuUInt8 *buffer, AuUInt32 length, std::function<void(bool)>) = 0;
|
||||
virtual bool WriteSync(const AuUInt8 *buffer, AuUInt32 length) = 0;
|
||||
// When BufferInputStreamAdhoc is called with false / in the default condition, read sync will be constrained by the async buffer
|
||||
// When BufferInputStreamAdhoc is called with true, you will block until you can get the requested data (all of it if, all = true; any of it, all = false)
|
||||
virtual bool ReadSync(const Memory::MemoryViewStreamWrite &memory, bool all = true) = 0;
|
||||
|
||||
// Writes max(cumulative memory.length per frame, (enableBufferedInput ? bufferedWriteSize : os page allowance)) to the sockets asynchronous stream
|
||||
// Returns false when no data whatsoever was written, generic error condition
|
||||
// Returns true when some data was collected, regardless of any errors that may have arose (defer to IServerSubscriber::OnClientError, IClientSubscriber::OnSocketError for error handling)
|
||||
virtual bool WriteAsync(const Memory::MemoryViewStreamRead &memory) = 0;
|
||||
|
||||
// Alternative WriteAsync method that calls a submission notification callback on flush on the dispatching thread
|
||||
virtual bool WriteAsync(const Memory::MemoryViewStreamRead &memory, AuUInt fence, const AuSPtr<ISocketSubmissionComplete> &input) = 0;
|
||||
|
||||
// When BufferInputStreamAdhoc is called with false / in the default condition, read sync will be constrained by the async buffer
|
||||
// When BufferInputStreamAdhoc is called with true, you will block until you can get the requested data (all of it if, all = true; any of it, all = false)
|
||||
virtual bool WriteSync(const Memory::MemoryViewStreamRead &memory) = 0;
|
||||
|
||||
/**
|
||||
* Sets the internal application buffer size
|
||||
* Noting that Linux's default network buffer looks like this:
|
||||
* Minimum, Initial, Maximum: 10240 87380 12582912 | 10KB, 86KB, 12MB
|
||||
*/
|
||||
virtual AuUInt GetInternalRingBuffer() = 0;
|
||||
virtual bool SetInternalRingBuffer(AuUInt bytes) = 0;
|
||||
virtual AuUInt GetInternalInputRingBuffer() = 0;
|
||||
virtual bool SetInternalInputRingBuffer(AuUInt bytes) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Defines the maximum amount of bytes each recieve frame can ingest
|
||||
*/
|
||||
virtual void SetRecvLength(AuOptional<AuUInt32> length) = 0;
|
||||
virtual void SetRecvLength(AuUInt32 length) = 0;
|
||||
virtual AuUInt32 GetRecvLength() = 0;
|
||||
|
||||
/**
|
||||
* Enable ad-hoc input buffer ingestion into our internal buffer
|
||||
* Only use when PumpRead will not suffice by design (ie: sync apps)
|
||||
*
|
||||
* When set to true, there is nothing preventing you from consuming a
|
||||
* DoS-worthy amount of bytes from the stream per frame
|
||||
*
|
||||
* This does not disable enableBufferedInput, it merely allows you
|
||||
* to read beyond enableBufferedInput/bufferedReadSize by accessing
|
||||
* the os stream page or hitting the network device inline
|
||||
*
|
||||
* There may be a performance bottleneck and/or gain depending on your
|
||||
* circumstances
|
||||
*
|
||||
* During high bandwidth transfers, you should set this to true
|
||||
*
|
||||
* Default: false (0)
|
||||
*/
|
||||
virtual void BufferAdhoc(AuOptional<bool> value) = 0;
|
||||
virtual void BufferInputStreamAdhoc(bool value) = 0;
|
||||
|
||||
virtual void ConfigureHasDataCallback(bool enabled) = 0;
|
||||
|
||||
virtual void ReconfigureStreams(const StreamConfig &config) = 0;
|
||||
};
|
||||
|
||||
class ILocalClientSocket : public IClientSocket
|
||||
struct ILocalClientSocket : public IClientSocket
|
||||
{
|
||||
public:
|
||||
using ConnectCallback_cb = std::function<void(bool success)>;
|
||||
virtual bool Connect() = 0;
|
||||
virtual void ConnectAsync(ConnectCallback_cb cb) = 0;
|
||||
// Connects to the endpoint defined in the ClientConfig
|
||||
// Completion will be notified by the following callbacks;
|
||||
// IClientSubscriber::OnServerConnectSuccess,
|
||||
// IClientSubscriber::OnServerConnectFailed
|
||||
// returns true on success
|
||||
virtual bool Connect() = 0;
|
||||
|
||||
// Completion will be notified by the following callbacks;
|
||||
// IClientSubscriber::OnServerConnectSuccess,
|
||||
// IClientSubscriber::OnServerConnectFailed
|
||||
//
|
||||
// ...on any worker thread under a generic pump or read only pump cycle
|
||||
virtual void ConnectAsync() = 0;
|
||||
};
|
||||
|
||||
struct TlsConnect
|
||||
@ -214,77 +349,121 @@ namespace Aurora::IO::Net
|
||||
AuSPtr<IBasicSocket> socket;
|
||||
};
|
||||
|
||||
using TlsConnect_cb = std::function<bool(const TlsConnect &info)>;
|
||||
|
||||
struct SocketConfig
|
||||
{
|
||||
StreamConfig stream;
|
||||
};
|
||||
|
||||
struct ServerInfo
|
||||
{
|
||||
Endpoint listen;
|
||||
ConnectionEndpoint listen;
|
||||
AuUInt32 maxSessions;
|
||||
SocketConfig clientDefaults;
|
||||
AuSPtr<IServerSubscriber> serverSubscriber;
|
||||
};
|
||||
|
||||
struct TLSServerInfo : ServerInfo
|
||||
{
|
||||
Aurora::Crypto::RSAPair cert;
|
||||
AuSPtr<IServerSubscriberTls> tlsServerSubscriber;
|
||||
};
|
||||
|
||||
struct ClientConfig : SocketConfig
|
||||
{
|
||||
SocketHostName socket;
|
||||
//AuString service;
|
||||
AuUInt16 port;
|
||||
bool enableHasDataCallback {true};
|
||||
AuSPtr<IClientSubscriber> clientSubscriber;
|
||||
};
|
||||
|
||||
struct ClientInfo
|
||||
struct TLSClientConfig : ClientConfig
|
||||
{
|
||||
Endpoint socket;
|
||||
AuString service;
|
||||
AuSPtr<IClientSubscriberTls> clientSubscriber;
|
||||
};
|
||||
|
||||
struct TLSClientInfo : ClientInfo
|
||||
struct IServer : public IBasicSocket
|
||||
{
|
||||
TlsConnect_cb pinning;
|
||||
};
|
||||
|
||||
class IServer : public IBasicSocket
|
||||
{
|
||||
public:
|
||||
virtual bool AddAcceptCallback(const InitializeSocket_cb &accept) = 0; // on accept*
|
||||
virtual bool AddExitCallback(const ShutdownSocket_cb &accept) = 0; // server shutdown*
|
||||
|
||||
virtual void GetClients(AuList<AuSPtr<IClientSocket>> &clients) = 0;
|
||||
virtual bool Listen() = 0;
|
||||
virtual void ReconfigureDefaultStream(const StreamConfig &config) = 0;
|
||||
};
|
||||
|
||||
class INetworkingPool
|
||||
|
||||
struct ISocketFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Supported thread models:
|
||||
while (true)
|
||||
{
|
||||
// read from sockets pre-frame
|
||||
PumpRead();
|
||||
|
||||
// process other async network logic here
|
||||
|
||||
// process writes and a few early reads before we sleep
|
||||
PumpWrite();
|
||||
|
||||
// yield
|
||||
Yield();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// process other async network logic here
|
||||
|
||||
// run once
|
||||
Pump()
|
||||
}
|
||||
|
||||
...and none with BufferAdhoc enabled
|
||||
*/
|
||||
virtual void PumpRead() = 0;
|
||||
virtual void PumpWrite() = 0;
|
||||
|
||||
virtual void Pump() = 0;
|
||||
|
||||
virtual bool NewServer(const ServerInfo &listen, AuSPtr<IServer> &out) = 0;
|
||||
virtual bool NewTlsServer(const TLSServerInfo &keys, AuSPtr<IServer> &out) = 0;
|
||||
|
||||
virtual bool NewClient(const ClientInfo &info, AuSPtr<ILocalClientSocket> &out) = 0;
|
||||
virtual bool NewTlsClient(const TLSClientInfo &info, AuSPtr<ILocalClientSocket> &out) = 0;
|
||||
virtual bool NewClient(const ClientConfig &info, AuSPtr<ILocalClientSocket> &out) = 0;
|
||||
virtual bool NewTlsClient(const TLSClientConfig &info, AuSPtr<ILocalClientSocket> &out) = 0;
|
||||
};
|
||||
|
||||
struct ServiceEndpoint
|
||||
{
|
||||
ETransportProtocol protocol;
|
||||
AuString hostname;
|
||||
AuString service;
|
||||
};
|
||||
|
||||
struct INetworkInterface
|
||||
{
|
||||
virtual IPEndpoint ResolveSocketSync(const SocketHostName &hostname, AuUInt16 port);
|
||||
virtual IPEndpoint ResolveServiceSync(const ServiceEndpoint &service);
|
||||
|
||||
virtual bool SendDatagramAsync(const ConnectionEndpoint &endpoint, const Memory::MemoryViewRead &memory);
|
||||
|
||||
virtual AuSPtr<ISocketFactory> GetSocketFactory() = 0;
|
||||
};
|
||||
|
||||
struct INetworkingPool
|
||||
{
|
||||
// A:
|
||||
virtual AuUInt32 Pump(int idx) = 0;
|
||||
|
||||
// B:
|
||||
virtual AuUInt32 PumpRead(int idx) = 0;
|
||||
virtual AuUInt32 PumpWrite(int idx) = 0;
|
||||
|
||||
// C:
|
||||
virtual AuUInt32 PollWorker(int idx) = 0;
|
||||
virtual AuUInt32 RunWorker(int idx, AuUInt32 timeout) = 0;
|
||||
|
||||
virtual AuUInt8 GetWorkers() = 0;
|
||||
|
||||
virtual AuSPtr<INetworkInterface> GetNetworkInterface() = 0;
|
||||
|
||||
virtual void Shutdown() = 0;
|
||||
};
|
||||
|
||||
AUE_DEFINE(ENetworkPoolModel, (
|
||||
|
||||
/// given a group id, uses prespawned async application event queues
|
||||
eAsyncApp,
|
||||
|
||||
///
|
||||
eAsyncThreadPool,
|
||||
|
||||
/// it just werks
|
||||
eInternalThreadPool,
|
||||
|
||||
/// Developer intends to call the INetworkingPoll poll functions with the respective thread id
|
||||
eUserCreateThreadRunnable
|
||||
))
|
||||
|
||||
struct NetworkPool
|
||||
{
|
||||
AuUInt8 workers {1};
|
||||
|
||||
ENetworkPoolModel mode;
|
||||
|
||||
AuUInt8 asyncWorkGroup;
|
||||
AuUInt8 asyncWorkerIdOffset {};
|
||||
|
||||
AuSPtr<Async::IThreadPool> threadPool;
|
||||
|
||||
/// Ignore me. Used on platforms that suck (win32) when the model is defined as eAsyncApp or eAsyncThreadPool
|
||||
AuUInt32 frequencyNotAsync {50};
|
||||
};
|
||||
|
||||
AUKN_SHARED_API(CreateNetworkPool, INetworkingPool, const NetworkPool & meta);
|
||||
}
|
@ -11,6 +11,7 @@ namespace Aurora::Locale
|
||||
{
|
||||
enum class ECodePage
|
||||
{
|
||||
eUnsupported,
|
||||
eUTF32,
|
||||
eUTF32BE,
|
||||
eUTF16,
|
||||
@ -23,7 +24,6 @@ namespace Aurora::Locale
|
||||
eSJIS,
|
||||
eLatin1,
|
||||
eSysUnk,
|
||||
eUnsupported,
|
||||
eMax = eUnsupported
|
||||
};
|
||||
}
|
@ -9,18 +9,32 @@
|
||||
|
||||
namespace Aurora::Locale::Encoding
|
||||
{
|
||||
AUKN_SYM AuStreamReadWrittenPair_t EncodeUTF8(const Memory::MemoryViewRead &utf8, const Memory::MemoryViewWrite &binary, ECodePage page = ECodePage::eUnsupported);
|
||||
|
||||
static inline AuStreamReadWrittenPair_t EncodeUTF8(const AuString &utf8, const Memory::MemoryViewWrite &binary, ECodePage page = ECodePage::eUnsupported)
|
||||
struct BOM
|
||||
{
|
||||
return EncodeUTF8(Memory::MemoryViewRead(utf8), binary, page);
|
||||
}
|
||||
ECodePage page;
|
||||
AuUInt8 length;
|
||||
};
|
||||
|
||||
AUKN_SYM std::optional<AuPair<ECodePage, AuUInt8>> DecodeBOM(const Memory::MemoryViewRead & binary);
|
||||
// Attempt to guess the code page signature of the string view provided by the binary view
|
||||
AUKN_SYM BOM DecodeBOM(const Memory::MemoryViewRead &binary);
|
||||
|
||||
/// Translates a buffer, possibly a slice of a stream, to UTF-8
|
||||
/// Returns a pair; bytes consumed, bytes written
|
||||
// General purpose arbitrary page to UTF8 (AuStrings are UTF-8 - not 16 or 32; bite me)
|
||||
AUKN_SYM AuStreamReadWrittenPair_t EncodeUTF8(const Memory::MemoryViewRead &utf8, const Memory::MemoryViewWrite &binary, ECodePage page = ECodePage::eUnsupported);
|
||||
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead &binary, const Memory::MemoryViewWrite &utf8, ECodePage page = ECodePage::eUnsupported);
|
||||
|
||||
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead &binary, AuString &out, ECodePage page = ECodePage::eUnsupported);
|
||||
|
||||
// Optimized UTF translation functions
|
||||
AUKN_SYM AuStreamReadWrittenPair_t ReadUTF32IntoUTF8ByteString(const Memory::MemoryViewRead &utf32, const Memory::MemoryViewWrite &utf8);
|
||||
AUKN_SYM AuStreamReadWrittenPair_t ReadUTF8IntoUTF32ByteString(const Memory::MemoryViewRead &utf8, const Memory::MemoryViewWrite &utf32);
|
||||
|
||||
// Endianswap functions, could be subject to SIMD optimizations
|
||||
AUKN_SYM void SwapUTF32(const Memory::MemoryViewWrite &utf32);
|
||||
AUKN_SYM void SwapUTF16(const Memory::MemoryViewWrite &utf16);
|
||||
|
||||
// Counst the amount of codepoints in a buffer, breaking when the stream is incomplete, giving you the accurate amount of bytes or relevant codepoints in a stream view
|
||||
AUKN_SYM AuUInt32 CountUTF32Length(const Memory::MemoryViewRead &utf32, bool bytes = false); // codepoint = U32 encoded; always 4 bytes per codepoint
|
||||
AUKN_SYM AuUInt32 CountUTF16Length(const Memory::MemoryViewRead &utf16, bool bytes = false); // codepoint = U32 encoded; at most: 4 bytes per codepoint, usual: 2 bytes
|
||||
AUKN_SYM AuUInt32 CountUTF8Length(const Memory::MemoryViewRead &utf8, bool bytes = false); // codepoint = U32 encoded; at most: 6 bytes per codepoint
|
||||
AUKN_SYM AuUInt32 CountSJISLength(const Memory::MemoryViewRead &sjis, bool bytes = false); // codepoint = one character
|
||||
AUKN_SYM AuUInt32 CountGBK16Length(const Memory::MemoryViewRead &gbk, bool bytes = false); // codepoint = at most; one GBK byte pair
|
||||
}
|
@ -193,7 +193,6 @@ namespace Aurora::Memory
|
||||
}
|
||||
}
|
||||
|
||||
auto oldptr = readPtr;
|
||||
auto skipped = Read(&out, sizeof(T));
|
||||
if (skipped != sizeof(T))
|
||||
{
|
||||
|
@ -14,15 +14,15 @@ namespace Aurora::Process
|
||||
{
|
||||
AUKN_SYM AU_NORETURN void Exit(AuUInt32 exitcode);
|
||||
|
||||
enum class EModulePath
|
||||
{
|
||||
AUE_DEFINE(EModulePath,
|
||||
(
|
||||
eModulePathCWD,
|
||||
eModulePathSystemDir, /// /lib/, windir/system32
|
||||
eModulePathUserDir, /// /usr/lib/
|
||||
eProcessDirectory, ///
|
||||
eOSSpecified, /// LD_LIBRARY_PATH + /etc/ld.so.conf, AddDllDirectory
|
||||
eSpecified
|
||||
};
|
||||
));
|
||||
|
||||
static AuList<EModulePath> kUserOverloadableSearchPath = {EModulePath::eSpecified, EModulePath::eModulePathCWD, EModulePath::eProcessDirectory, EModulePath::eModulePathUserDir, EModulePath::eModulePathSystemDir};
|
||||
static AuList<EModulePath> kAdminOverloadableSearchPath = {EModulePath::eSpecified, EModulePath::eModulePathSystemDir, EModulePath::eProcessDirectory, EModulePath::eModulePathCWD, EModulePath::eModulePathUserDir};
|
||||
@ -30,12 +30,13 @@ namespace Aurora::Process
|
||||
struct ModuleLoadRequest
|
||||
{
|
||||
AuString mod;
|
||||
AuOptional<AuString> version;
|
||||
AuOptional<AuString> extension;
|
||||
AuString version;
|
||||
AuString extension;
|
||||
AuList<AuString> const *specifiedSearchPaths {};
|
||||
AuList<EModulePath> const *searchPath {};
|
||||
bool verifyOne {}; // always true if the executable is signed
|
||||
bool verifyAll {};
|
||||
bool verify {}; // always effectively true if the executable is signed and enableMitigations is true
|
||||
bool unixCheckPlusX {};
|
||||
bool enableMitigations {true};
|
||||
};
|
||||
|
||||
AUKN_SYM bool LoadModule(const ModuleLoadRequest &request);
|
||||
|
@ -30,6 +30,7 @@ namespace Aurora::Processes
|
||||
/// @param exitOnSpawn atomically destroys the current process <br>
|
||||
/// some platforms only support this
|
||||
///
|
||||
// TODO(Reece): what in the hell this is ugly
|
||||
virtual bool Start(enum ESpawnType, bool fwdOut, bool fwdErr, bool fwdIn) = 0;
|
||||
|
||||
virtual bool Terminate() = 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/***
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IRandomDevice.hpp
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <AuroraForEach.hpp>
|
||||
#include <AuroraInterfaces.hpp>
|
||||
#include <AuroraEnum.hpp>
|
||||
|
||||
#include "../AuroraMacros.hpp"
|
||||
|
||||
@ -81,6 +82,7 @@ namespace AuData = Aurora::Data;
|
||||
namespace AuDebug = Aurora::Debug;
|
||||
namespace AuThreading = Aurora::Threading;
|
||||
namespace AuThreadPrimitives = Aurora::Threading::Primitives;
|
||||
namespace AuThreads = Aurora::Threading::Threads;
|
||||
namespace AuHwInfo = Aurora::HWInfo;
|
||||
namespace AuIO = Aurora::IO;
|
||||
namespace AuIOFS = Aurora::IO::FS;
|
||||
@ -95,6 +97,9 @@ namespace AuTypes = Aurora::Types;
|
||||
namespace AuLog = Aurora::Console::Logging;
|
||||
namespace AuMemory = Aurora::Memory;
|
||||
|
||||
using AuWorkerId_t = AuAsync::WorkerId_t;
|
||||
using AuWorkerPId_t = AuAsync::WorkerPId_t;
|
||||
|
||||
static inline void AuDebugBreak()
|
||||
{
|
||||
AuDebug::DebugBreak();
|
||||
@ -108,6 +113,11 @@ namespace Aurora
|
||||
bool autoCompressOldLogs {false};
|
||||
AuUInt32 maxSizeMB {128 * 1024 * 1024}; // these numbers feel insane, but at least we have a max threshold
|
||||
int maxLogs {1024}; // by default, we neither leak disk space or waste opportunities of being able to dig through old data
|
||||
#if defined(SHIP)
|
||||
bool writeLogsToUserDir {true}; // use user directory
|
||||
#else
|
||||
bool writeLogsToUserDir {false}; // use cwd
|
||||
#endif
|
||||
};
|
||||
|
||||
struct TelemetryConfigDoc
|
||||
@ -137,13 +147,21 @@ namespace Aurora
|
||||
TelemetryConfigDoc defaultConfig;
|
||||
};
|
||||
|
||||
struct SocketConsole
|
||||
{
|
||||
bool enableLogging {false};
|
||||
bool binaryProto {false};
|
||||
AuString path;
|
||||
AuIONet::ConnectionEndpoint net;
|
||||
};
|
||||
|
||||
struct ConsoleConfig
|
||||
{
|
||||
/// Enables Aurora::Console::xxxStd functions; defer to enableStdXX for default logger behaviour
|
||||
bool enableStdPassthrough {};
|
||||
bool enableStdPassthrough {false};
|
||||
|
||||
/// Disables standard, debug, and GUI consoles
|
||||
bool disableAllConsoles {};
|
||||
/// Enables standard, debug, and GUI consoles
|
||||
bool enableConsole {true};
|
||||
|
||||
/// Attempt to force a terminal emulator host under graphical subsystems
|
||||
bool forceConsoleWindow {};
|
||||
@ -151,7 +169,7 @@ namespace Aurora
|
||||
/// Attempt to force a GUI console under command line targets
|
||||
bool forceToolKitWindow {};
|
||||
|
||||
/// In conjunction with enableStdPassthrough, enables Aurora::Console::ReadStd to read binary
|
||||
/// In conjunction with enableStdPassthrough, Aurora::Console::ReadStd reads a binary stream
|
||||
/// In conjunction with !enableStdPassthrough, enables stdout logging
|
||||
bool enableStdIn {true};
|
||||
|
||||
@ -162,14 +180,31 @@ namespace Aurora
|
||||
/// Use WxWidgets when possible
|
||||
bool enableWxWidgets {true};
|
||||
|
||||
#if 1
|
||||
/// FIO config
|
||||
LocalLogInfo fio;
|
||||
#endif
|
||||
|
||||
AuString titleBrand = "Aurora SDK Sample";
|
||||
|
||||
AuString supportPublic {"https://git.reece.sx/AuroraSupport/AuroraRuntime/issues"};
|
||||
AuString supportInternal {"https://jira.reece.sx"};
|
||||
};
|
||||
|
||||
struct LoggerConfig
|
||||
{
|
||||
///
|
||||
bool enableStdIn {true};
|
||||
|
||||
///
|
||||
bool enableStdOut {true};
|
||||
|
||||
/// FIO config
|
||||
LocalLogInfo fileConfiguration;
|
||||
|
||||
/// Socket config
|
||||
SocketConsole socketConfiguration;
|
||||
};
|
||||
|
||||
struct CryptoConfig
|
||||
{
|
||||
@ -192,13 +227,16 @@ namespace Aurora
|
||||
|
||||
struct AsyncConfig
|
||||
{
|
||||
bool enableSchedularThread {true}; // turn this off to make your application lighter weight
|
||||
AuUInt32 threadPoolDefaultStackSize {};
|
||||
AuUInt32 schedularFrequency {2}; // * 0.5 or 1 MS depending on the platform
|
||||
AuUInt32 sysPumpFrequency {25}; // x amount of schedularFrequencys
|
||||
};
|
||||
|
||||
struct FIOConfig
|
||||
{
|
||||
AuOptional<AuString> defaultBrand = "Aurora";
|
||||
/// You can bypass branding by assigning an empty string to 'defaultBrand'
|
||||
AuString defaultBrand = "Aurora";
|
||||
};
|
||||
|
||||
struct RuntimeStartInfo
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
namespace Aurora::Threading::Threads
|
||||
{
|
||||
enum class EThreadPrio
|
||||
{
|
||||
AUE_DEFINE(EThreadPrio,
|
||||
(
|
||||
eInvalid,
|
||||
ePrioAboveHigh,
|
||||
ePrioHigh,
|
||||
ePrioNormal,
|
||||
ePrioSub,
|
||||
ePrioRT
|
||||
};
|
||||
));
|
||||
}
|
@ -34,6 +34,11 @@ namespace Aurora::Threading::Threads
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
T operator *()
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
TLSVariable& operator =(const T & val)
|
||||
{
|
||||
|
@ -14,10 +14,10 @@ namespace Aurora::Threading::Threads
|
||||
ThreadInfo()
|
||||
{}
|
||||
|
||||
ThreadInfo(const AuSPtr<IThreadVectors> &callbacks) : callbacks(callbacks)
|
||||
ThreadInfo(const AuSPtr<IThreadVectors> &callbacks) : callbacks(callbacks), stackSize(0)
|
||||
{}
|
||||
|
||||
ThreadInfo(const AuSPtr<IThreadVectors> &callbacks, const AuString &name) : callbacks(callbacks), name(name)
|
||||
ThreadInfo(const AuSPtr<IThreadVectors> &callbacks, const AuString &name) : callbacks(callbacks), name(name), stackSize(0)
|
||||
{}
|
||||
|
||||
ThreadInfo(const AuSPtr<IThreadVectors> &callbacks, const AuString &name, AuUInt32 stackSize) : callbacks(callbacks), name(name), stackSize(stackSize)
|
||||
@ -27,7 +27,7 @@ namespace Aurora::Threading::Threads
|
||||
{}
|
||||
|
||||
AuSPtr<IThreadVectors> callbacks;
|
||||
AuOptional<AuUInt32> stackSize;
|
||||
AuUInt32 stackSize;
|
||||
AuOptional<AuString> name;
|
||||
};
|
||||
}
|
@ -99,7 +99,11 @@ using AuWPtr = AURORA_RUNTIME_AU_WEAK_PTR<T>;
|
||||
#define AURORA_RUNTIME_AU_UNIQUE_PTR std::unique_ptr
|
||||
#endif
|
||||
|
||||
template<typename T, typename Deleter_t>
|
||||
#if !defined(AURORA_RUNTIME_AU_DEFAULT_DELETER)
|
||||
#define AURORA_RUNTIME_AU_DEFAULT_DELETER = std::default_delete<T>
|
||||
#endif
|
||||
|
||||
template<typename T, typename Deleter_t AURORA_RUNTIME_AU_DEFAULT_DELETER>
|
||||
using AuUPtr = AURORA_RUNTIME_AU_UNIQUE_PTR<T, Deleter_t>;
|
||||
|
||||
#if !defined(AURORA_RUNTIME_AU_PAIR)
|
||||
|
@ -550,6 +550,18 @@ struct is_base_of_template_impl_au
|
||||
template < template <typename...> class base,typename derived>
|
||||
using AuIsBaseOfTemplate = typename is_base_of_template_impl_au<base,derived>::type;
|
||||
|
||||
template <typename Tuple, std::size_t ... Is>
|
||||
auto AuTuplePopFrontImpl(const Tuple& tuple, std::index_sequence<Is...>)
|
||||
{
|
||||
return std::make_tuple(std::get<1 + Is>(tuple)...);
|
||||
}
|
||||
|
||||
template <typename Tuple>
|
||||
auto AuTuplePopFront(const Tuple& tuple)
|
||||
{
|
||||
return AuTuplePopFrontImpl(tuple, std::make_index_sequence<std::tuple_size<Tuple>::value - 1>());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool AuTestBit(T value, AuUInt8 idx)
|
||||
{
|
||||
@ -568,6 +580,9 @@ static inline void AuClearBit(T &value, AuUInt8 idx)
|
||||
value &= ~(T(1) << T(idx));
|
||||
}
|
||||
|
||||
// TODO: AuPopCnt
|
||||
// TODO: AuBitScanForward
|
||||
|
||||
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_ARM)
|
||||
#define AURORA_PERMIT_ARBITRARY_REF
|
||||
#endif
|
||||
|
@ -8,16 +8,19 @@
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "Async.hpp"
|
||||
#include "Schedular.hpp"
|
||||
#include "AsyncApp.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
void InitAsync()
|
||||
{
|
||||
InitSched();
|
||||
InitApp();
|
||||
}
|
||||
|
||||
void ShutdownAsync()
|
||||
{
|
||||
DeinitSched();
|
||||
ReleaseApp();
|
||||
}
|
||||
}
|
@ -7,41 +7,12 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "GroupState.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
#include "AsyncRunnable.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
class IAsyncRunnable
|
||||
{
|
||||
public:
|
||||
virtual void RunAsync() = 0;
|
||||
|
||||
virtual void CancelAsync() {}
|
||||
};
|
||||
|
||||
class AsyncFuncRunnable : public IAsyncRunnable
|
||||
{
|
||||
public:
|
||||
|
||||
std::function<void()> callback;
|
||||
|
||||
AsyncFuncRunnable(std::function<void()> &&callback) : callback(std::move(callback))
|
||||
{}
|
||||
|
||||
AsyncFuncRunnable(const std::function<void()> &callback) : callback(callback)
|
||||
{}
|
||||
|
||||
void RunAsync() override
|
||||
{
|
||||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void InitAsync();
|
||||
void ShutdownAsync();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -7,87 +7,46 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "ThreadPool.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct GroupState;
|
||||
struct ThreadState;
|
||||
//class WorkItem;
|
||||
void InitApp();
|
||||
void ReleaseApp();
|
||||
|
||||
|
||||
void DecRunningTasks();
|
||||
void IncRunningTasks();
|
||||
|
||||
class AsyncApp : public IAsyncApp
|
||||
struct AsyncApp : public IAsyncApp, ThreadPool
|
||||
{
|
||||
public:
|
||||
AsyncApp();
|
||||
bool Spawn(WorkerId_t workerId) override;
|
||||
void SetRunningMode(bool eventRunning) override;
|
||||
bool Create(WorkerId_t workerId) override;
|
||||
bool InRunnerMode() override;
|
||||
bool Poll() override;
|
||||
bool RunOnce() override;
|
||||
bool Run() override;
|
||||
void Shutdown() override;
|
||||
bool Exiting() override;
|
||||
AuSPtr<IWorkItem> NewWorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking) override;
|
||||
AuSPtr<IWorkItem> NewFence() override;
|
||||
Threading::Threads::ThreadShared_t ResolveHandle(WorkerId_t) override;
|
||||
AuBST<ThreadGroup_t, AuList<ThreadId_t>> GetThreads() override;
|
||||
WorkerId_t GetCurrentThread() override;
|
||||
bool Sync(WorkerId_t workerId, AuUInt32 timeoutMs = 0, bool requireSignal = false) override;
|
||||
void Signal(WorkerId_t workerId) override;
|
||||
void SyncAllSafe() override;
|
||||
void AddFeature(WorkerId_t id, AuSPtr<Threading::Threads::IThreadFeature> feature, bool async) override;
|
||||
void AssertInThreadGroup(ThreadGroup_t group) override;
|
||||
void AssertWorker(WorkerId_t id) override;
|
||||
bool ScheduleLoopSource(const AuSPtr<Loop::ILoopSource> &loopSource, WorkerId_t workerId, AuUInt32 timeout, const AuConsumer<AuSPtr<Loop::ILoopSource>, bool> &callback) override;
|
||||
|
||||
|
||||
// Main thread logic
|
||||
void Start() override;
|
||||
void Main() override;
|
||||
void Shutdown() override;
|
||||
bool Exiting() override;
|
||||
void SetConsoleCommandDispatcher(WorkerId_t id) override;
|
||||
|
||||
// Spawning
|
||||
bool Spawn(WorkerId_t workerId) override;
|
||||
void SetWorkerIdIsThreadRunner(WorkerId_t, bool runner) override;
|
||||
void CleanUpWorker(WorkerId_t wid) override;
|
||||
void CleanWorkerPoolReservedZeroFree() override;
|
||||
|
||||
Threading::Threads::ThreadShared_t ResolveHandle(WorkerId_t) override;
|
||||
|
||||
AuBST<ThreadGroup_t, AuList<ThreadId_t>> GetThreads() override;
|
||||
WorkerId_t GetCurrentThread() override;
|
||||
|
||||
// Synchronization
|
||||
bool Sync(WorkerId_t group, AuUInt32 timeoutMs, bool requireSignal) override;
|
||||
void Signal(WorkerId_t group) override;
|
||||
|
||||
bool WaitFor(WorkerId_t unlocker, const AuSPtr<Threading::IWaitable> &primitive, AuUInt32 ms); // when unlocker = this, pump event loop
|
||||
//bool WaitFor(DispatchTarget_t unlocker, Threading::IWaitable *primitive, AuUInt32 ms) override; // when unlocker = this, pump event loop
|
||||
|
||||
void SyncAllSafe() override;
|
||||
|
||||
// Features
|
||||
void AddFeature(WorkerId_t id, AuSPtr<Threading::Threads::IThreadFeature> feature, bool async = false) override;
|
||||
|
||||
// Debug
|
||||
void AssertInThreadGroup(ThreadGroup_t group) override;
|
||||
void AssertWorker(WorkerId_t id) override;
|
||||
|
||||
void Run(WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
|
||||
|
||||
bool Poll(bool block) override;
|
||||
bool PollInternal(bool block);
|
||||
bool PollLoopSource(bool block);
|
||||
|
||||
size_t GetThreadWorkersCount(ThreadGroup_t group);
|
||||
|
||||
bool CtxYield();
|
||||
int CfxPollPush();
|
||||
void CtxPollReturn(const AuSPtr<ThreadState> &state, int status, bool hitTask);
|
||||
bool ScheduleLoopSource(const AuSPtr<Loop::ILoopSource> &loopSource, WorkerId_t workerId, AuUInt32 timeout, const AuConsumer<AuSPtr<Loop::ILoopSource>, bool> &callback) override;
|
||||
|
||||
private:
|
||||
AuSPtr<ThreadState> GetThreadHandle(WorkerId_t id);
|
||||
|
||||
void ThisExiting();
|
||||
void ShutdownZero();
|
||||
|
||||
// TODO: BarrierMultiple
|
||||
bool Barrier(WorkerId_t, AuUInt32 ms, bool requireSignal, bool drop);
|
||||
|
||||
AuThreadPrimitives::RWLockUnique_t rwlock_;
|
||||
|
||||
AuSPtr<GroupState> GetGroup(ThreadGroup_t type);
|
||||
|
||||
AuSPtr<ThreadState> GetThreadState();
|
||||
|
||||
void Entrypoint(WorkerId_t id);
|
||||
|
||||
using ThreadDb_t = AuBST<ThreadGroup_t, AuSPtr<GroupState>>;
|
||||
|
||||
ThreadDb_t threads_;
|
||||
bool shuttingdown_ {};
|
||||
AuOptional<WorkerId_t> commandDispatcher_;
|
||||
};
|
||||
}
|
75
Source/Async/AsyncRunnable.hpp
Normal file
75
Source/Async/AsyncRunnable.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AsyncRunnable.hpp
|
||||
Date: 2021-11-2
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
class IAsyncRunnable
|
||||
{
|
||||
public:
|
||||
virtual float GetPrio() { return 0.5f; };
|
||||
virtual void RunAsync() = 0;
|
||||
|
||||
virtual void CancelAsync() {}
|
||||
};
|
||||
|
||||
class AsyncFuncRunnable : public IAsyncRunnable
|
||||
{
|
||||
public:
|
||||
|
||||
std::function<void()> callback;
|
||||
std::function<void()> fail;
|
||||
AuThreadPrimitives::SpinLock lock;
|
||||
|
||||
AsyncFuncRunnable(std::function<void()> &&callback) : callback(std::move(callback))
|
||||
{}
|
||||
|
||||
AsyncFuncRunnable(std::function<void()> &&callback, std::function<void()> &&fail) : callback(std::move(callback)), fail(std::move(fail))
|
||||
{}
|
||||
|
||||
AsyncFuncRunnable(const std::function<void()> &callback) : callback(callback)
|
||||
{}
|
||||
|
||||
AsyncFuncRunnable(const std::function<void()> &callback, const std::function<void()> &fail) : callback(callback), fail(fail)
|
||||
{}
|
||||
|
||||
void RunAsync() override
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
SysAssertDbgExp(callback, "Missing callback std::function");
|
||||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
}
|
||||
fail = {};
|
||||
callback = {};
|
||||
}
|
||||
|
||||
void CancelAsync() override
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
if (fail)
|
||||
{
|
||||
try
|
||||
{
|
||||
fail();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
}
|
||||
}
|
||||
fail = {};
|
||||
callback = {};
|
||||
}
|
||||
};
|
||||
}
|
35
Source/Async/GroupState.cpp
Normal file
35
Source/Async/GroupState.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: GroupState.cpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#include <RuntimeInternal.hpp>
|
||||
#include "Async.hpp"
|
||||
#include "GroupState.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
bool GroupState::Init()
|
||||
{
|
||||
this->cvWorkMutex = AuThreadPrimitives::ConditionMutexUnique();
|
||||
if (!this->cvWorkMutex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->cvVariable = AuThreadPrimitives::ConditionVariableUnique(AuUnsafeRaiiToShared(this->cvWorkMutex));
|
||||
if (!this->cvVariable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->eventLs = Loop::NewLSEvent(false, false, true);
|
||||
if (!this->eventLs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
34
Source/Async/GroupState.hpp
Normal file
34
Source/Async/GroupState.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: GroupState.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
#include "ThreadState.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct GroupState
|
||||
{
|
||||
ThreadGroup_t group;
|
||||
|
||||
AuThreadPrimitives::ConditionMutexUnique_t cvWorkMutex;
|
||||
AuThreadPrimitives::ConditionVariableUnique_t cvVariable;
|
||||
AuSPtr<Loop::ILSEvent> eventLs;
|
||||
|
||||
AuList<WorkEntry_t> workQueue;
|
||||
bool sorted {};
|
||||
AuUInt32 dirty {};
|
||||
|
||||
AuBST<ThreadId_t, AuSPtr<ThreadState>> workers;
|
||||
|
||||
bool Init();
|
||||
|
||||
bool inline IsSysThread()
|
||||
{
|
||||
return group == 0;
|
||||
}
|
||||
};
|
||||
}
|
@ -8,7 +8,8 @@
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "Async.hpp"
|
||||
#include "Schedular.hpp"
|
||||
#include "AsyncApp.hpp"
|
||||
//#include "AsyncApp.hpp"
|
||||
#include "ThreadPool.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
@ -17,9 +18,10 @@ namespace Aurora::Async
|
||||
AuUInt64 ns;
|
||||
WorkerId_t target;
|
||||
AuSPtr<IAsyncRunnable> runnable;
|
||||
IThreadPoolInternal *pool;
|
||||
};
|
||||
|
||||
static Threading::Threads::ThreadUnique_t gThread;
|
||||
static AuThreads::ThreadUnique_t gThread;
|
||||
static AuThreadPrimitives::MutexUnique_t gSchedLock;
|
||||
static AuList<SchedEntry> gEntries;
|
||||
|
||||
@ -56,7 +58,7 @@ namespace Aurora::Async
|
||||
AuUInt32 counter {};
|
||||
AuList<SchedEntry> pending;
|
||||
|
||||
auto thread = Threading::Threads::GetThread();
|
||||
auto thread = AuThreads::GetThread();
|
||||
|
||||
while (!thread->Exiting())
|
||||
{
|
||||
@ -70,25 +72,29 @@ namespace Aurora::Async
|
||||
{
|
||||
try
|
||||
{
|
||||
static_cast<AsyncApp *>(GetAsyncApp())->Run(entry.target, entry.runnable);
|
||||
DecRunningTasks();
|
||||
entry.pool->Run(entry.target, entry.runnable);
|
||||
entry.pool->DecrementTasksRunning();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogWarn("Dropped scheduled task! Expect a leaky counter!");
|
||||
LogWarn("Would you rather `Why u no exit?!` or `WHY DID U JUST CRASH REEEE` in production?");
|
||||
if (entry.pool->ToThreadPool()->InRunnerMode())
|
||||
{
|
||||
LogWarn("Dropped scheduled task! Expect a leaky counter!");
|
||||
LogWarn("Would you rather `Why u no exit?!` or `WHY DID U JUST CRASH REEEE` in production?");
|
||||
}
|
||||
Debug::PrintError();
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
|
||||
if ((!gRuntimeConfig.async.sysPumpFrequency) || ((gRuntimeConfig.async.sysPumpFrequency) && (counter % gRuntimeConfig.async.sysPumpFrequency) == 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!std::exchange(gLockedPump, true))
|
||||
{
|
||||
NewWorkItem({0, 0}, AuMakeShared<BasicWorkStdFunc>(PumpSysThread))->Dispatch();
|
||||
NewWorkItem(AuWorkerId_t{0, 0}, AuMakeShared<BasicWorkStdFunc>(PumpSysThread))->Dispatch();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@ -98,7 +104,6 @@ namespace Aurora::Async
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitSched()
|
||||
@ -114,32 +119,41 @@ namespace Aurora::Async
|
||||
|
||||
void StartSched()
|
||||
{
|
||||
gThread = Threading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo(
|
||||
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(SchedThread)),
|
||||
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{})
|
||||
AU_LOCK_GUARD(gSchedLock);
|
||||
if (gThread) return;
|
||||
|
||||
gThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
|
||||
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(SchedThread)),
|
||||
AuThreads::IThreadVectorsFunctional::OnExit_t{})
|
||||
));
|
||||
|
||||
gThread->Run();
|
||||
}
|
||||
|
||||
/// @deprecated
|
||||
void StopSched()
|
||||
{
|
||||
gThread.reset();
|
||||
// intentionally no-op
|
||||
#if 0
|
||||
gThread.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Schedule(AuUInt64 ns, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable)
|
||||
void Schedule(AuUInt64 ns, IThreadPoolInternal *pool, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable)
|
||||
{
|
||||
AU_LOCK_GUARD(gSchedLock);
|
||||
IncRunningTasks();
|
||||
gEntries.push_back({ns, target, runnable});
|
||||
pool->IncrementTasksRunning();
|
||||
gEntries.push_back({ns, target, runnable, pool});
|
||||
}
|
||||
|
||||
void TerminateSceduledTasks(WorkerId_t target)
|
||||
void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target)
|
||||
{
|
||||
AU_LOCK_GUARD(gSchedLock);
|
||||
|
||||
for (auto itr = gEntries.begin(); itr != gEntries.end(); )
|
||||
{
|
||||
if (itr->target <= target)
|
||||
if ((itr->pool == pool) &&
|
||||
((itr->target == target) || (target.second == Async::kThreadIdAny && target.first == itr->target.first)))
|
||||
{
|
||||
itr->runnable->CancelAsync();
|
||||
itr = gEntries.erase(itr);
|
||||
|
@ -9,11 +9,13 @@
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct IThreadPoolInternal;
|
||||
|
||||
void InitSched();
|
||||
void DeinitSched();
|
||||
void StartSched();
|
||||
void StopSched();
|
||||
|
||||
void Schedule(AuUInt64 ns, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
|
||||
void TerminateSceduledTasks(WorkerId_t target);
|
||||
void Schedule(AuUInt64 ns, IThreadPoolInternal *pool, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
|
||||
void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target);
|
||||
}
|
1061
Source/Async/ThreadPool.cpp
Normal file
1061
Source/Async/ThreadPool.cpp
Normal file
File diff suppressed because it is too large
Load Diff
127
Source/Async/ThreadPool.hpp
Normal file
127
Source/Async/ThreadPool.hpp
Normal file
@ -0,0 +1,127 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: ThreadPool.hpp
|
||||
Date: 2021-10-30
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
struct GroupState;
|
||||
struct ThreadState;
|
||||
//class WorkItem;
|
||||
|
||||
struct IThreadPoolInternal
|
||||
{
|
||||
virtual bool WaitFor(WorkerId_t unlocker, const AuSPtr<Threading::IWaitable> &primitive, AuUInt32 ms) = 0;
|
||||
virtual void Run(WorkerId_t target, AuSPtr<IAsyncRunnable> runnable) = 0;
|
||||
virtual IThreadPool *ToThreadPool() = 0;
|
||||
virtual void IncrementTasksRunning() = 0;
|
||||
virtual void DecrementTasksRunning() = 0;
|
||||
};
|
||||
|
||||
|
||||
struct ThreadPool : public IThreadPool, public IThreadPoolInternal, std::enable_shared_from_this<ThreadPool>
|
||||
{
|
||||
ThreadPool();
|
||||
|
||||
// IThreadPoolInternal
|
||||
bool WaitFor(WorkerId_t unlocker, const AuSPtr<Threading::IWaitable> &primitive, AuUInt32 ms) override;
|
||||
void Run(WorkerId_t target, AuSPtr<IAsyncRunnable> runnable) override;
|
||||
IThreadPool *ToThreadPool() override;
|
||||
void IncrementTasksRunning() override;
|
||||
void DecrementTasksRunning() override;
|
||||
|
||||
// IThreadPool
|
||||
virtual bool Spawn(WorkerId_t workerId) override;
|
||||
|
||||
virtual void SetRunningMode(bool eventRunning) override;
|
||||
|
||||
virtual bool Create(WorkerId_t workerId) override;
|
||||
|
||||
virtual bool InRunnerMode() override;
|
||||
|
||||
virtual bool Poll() override;
|
||||
virtual bool RunOnce() override;
|
||||
virtual bool Run() override;
|
||||
|
||||
virtual void Shutdown() override;
|
||||
virtual bool Exiting() override;
|
||||
|
||||
virtual AuSPtr<IWorkItem> NewWorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking) override;
|
||||
virtual AuSPtr<IWorkItem> NewFence() override;
|
||||
|
||||
virtual Threading::Threads::ThreadShared_t ResolveHandle(WorkerId_t) override;
|
||||
|
||||
virtual AuBST<ThreadGroup_t, AuList<ThreadId_t>> GetThreads() override;
|
||||
|
||||
virtual WorkerId_t GetCurrentThread() override;
|
||||
|
||||
virtual bool Sync(WorkerId_t workerId, AuUInt32 timeoutMs, bool requireSignal) override;
|
||||
virtual void Signal(WorkerId_t workerId) override;
|
||||
virtual void SyncAllSafe() override;
|
||||
|
||||
virtual void AddFeature(WorkerId_t id, AuSPtr<Threading::Threads::IThreadFeature> feature, bool async) override;
|
||||
|
||||
virtual void AssertInThreadGroup(ThreadGroup_t group) override;
|
||||
virtual void AssertWorker(WorkerId_t id) override;
|
||||
|
||||
virtual bool ScheduleLoopSource(const AuSPtr<Loop::ILoopSource> &loopSource, WorkerId_t workerId, AuUInt32 timeout, const AuConsumer<AuSPtr<Loop::ILoopSource>, bool> &callback) override;
|
||||
|
||||
// Internal API
|
||||
|
||||
bool Spawn(WorkerId_t workerId, bool create);
|
||||
|
||||
bool InternalRunOne(bool block);
|
||||
bool PollInternal(bool block);
|
||||
bool PollLoopSource(bool block);
|
||||
|
||||
size_t GetThreadWorkersCount(ThreadGroup_t group);
|
||||
|
||||
virtual void CleanUpWorker(WorkerId_t wid) {};
|
||||
virtual void CleanWorkerPoolReservedZeroFree() {}; // calls shutdown under async apps
|
||||
|
||||
// Secret old fiber api
|
||||
bool CtxYield();
|
||||
int CtxPollPush();
|
||||
void CtxPollReturn(const AuSPtr<ThreadState> &state, int status, bool hitTask);
|
||||
|
||||
// TLS handle
|
||||
struct WorkerWPId_t : WorkerId_t
|
||||
{
|
||||
WorkerWPId_t()
|
||||
{}
|
||||
|
||||
WorkerWPId_t(const WorkerPId_t &ref) : WorkerId_t(ref.first, ref.second), pool(ref.pool)
|
||||
{}
|
||||
|
||||
AuWPtr<IThreadPool> pool;
|
||||
};
|
||||
|
||||
AuThreads::TLSVariable<WorkerWPId_t> tlsWorkerId;
|
||||
|
||||
private:
|
||||
// TODO: BarrierMultiple
|
||||
bool Barrier(WorkerId_t, AuUInt32 ms, bool requireSignal, bool drop);
|
||||
|
||||
protected:
|
||||
void Entrypoint(WorkerId_t id);
|
||||
|
||||
private:
|
||||
void ThisExiting();
|
||||
|
||||
AuSPtr<GroupState> GetGroup(ThreadGroup_t type);
|
||||
AuSPtr<ThreadState> GetThreadState();
|
||||
AuSPtr<ThreadState> GetThreadHandle(WorkerId_t id);
|
||||
|
||||
using ThreadDb_t = AuBST<ThreadGroup_t, AuSPtr<GroupState>>;
|
||||
|
||||
ThreadDb_t threads_;
|
||||
bool shuttingdown_ {};
|
||||
AuThreadPrimitives::RWLockUnique_t rwlock_;
|
||||
std::atomic_int tasksRunning_;
|
||||
bool runnersRunning_;
|
||||
};
|
||||
}
|
56
Source/Async/ThreadState.hpp
Normal file
56
Source/Async/ThreadState.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: ThreadState.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "AsyncRunnable.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
using WorkEntry_t = AuPair<ThreadId_t, AuSPtr<IAsyncRunnable>>;
|
||||
|
||||
// TODO: this is a hack because i havent implemented an epoll abstraction yet
|
||||
struct AsyncAppWaitSourceRequest
|
||||
{
|
||||
AuConsumer<AuSPtr<Loop::ILoopSource>, bool> callback;
|
||||
AuSPtr<Loop::ILoopSource> loopSource;
|
||||
AuUInt32 requestedOffset;
|
||||
AuUInt64 startTime;
|
||||
AuUInt64 endTime;
|
||||
};
|
||||
|
||||
struct GroupState;
|
||||
|
||||
struct ThreadState
|
||||
{
|
||||
WorkerId_t id;
|
||||
|
||||
//bool eventDriven {};
|
||||
|
||||
AuUInt8 multipopCount = 1;
|
||||
AuUInt32 lastFrameTime {};
|
||||
|
||||
AuThreads::ThreadShared_t threadObject;
|
||||
|
||||
//std::stack<jmp_buf> jmpStack;
|
||||
AuWPtr<GroupState> parent;
|
||||
|
||||
AuThreadPrimitives::SemaphoreUnique_t syncSema;
|
||||
AuList<AuSPtr<AuThreads::IThreadFeature>> features;
|
||||
|
||||
bool rejecting {};
|
||||
bool exiting {};
|
||||
bool inLoopSourceMode {};
|
||||
bool shuttingdown {};
|
||||
|
||||
AuThreadPrimitives::EventUnique_t running;
|
||||
//bool running;
|
||||
|
||||
AuList<AsyncAppWaitSourceRequest> loopSources;
|
||||
AuList<WorkEntry_t> pendingWorkItems;
|
||||
};
|
||||
}
|
@ -13,12 +13,12 @@
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
WorkItem::WorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking) : worker_(worker), task_(task)
|
||||
WorkItem::WorkItem(IThreadPoolInternal *owner, const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking) : worker_(worker), task_(task), owner_(owner)
|
||||
{
|
||||
if (supportsBlocking)
|
||||
{
|
||||
finishedEvent_ = AuThreadPrimitives::EventUnique(false, true, true);
|
||||
SysAssert(finishedEvent_);
|
||||
this->finishedEvent_ = AuThreadPrimitives::EventUnique(false, true, true);
|
||||
SysAssert(this->finishedEvent_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ namespace Aurora::Async
|
||||
{
|
||||
auto dependency = std::reinterpret_pointer_cast<WorkItem>(workItem);
|
||||
|
||||
AU_LOCK_GUARD(lock);
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
AU_LOCK_GUARD(dependency->lock);
|
||||
|
||||
if (dependency->HasFailed())
|
||||
@ -43,7 +43,7 @@ namespace Aurora::Async
|
||||
}
|
||||
|
||||
dependency->waiters_.push_back(shared_from_this());
|
||||
waitOn_.push_back(workItem);
|
||||
this->waitOn_.push_back(workItem);
|
||||
}
|
||||
|
||||
if (status)
|
||||
@ -53,14 +53,13 @@ namespace Aurora::Async
|
||||
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::WaitFor(const AuList<AuSPtr<IWorkItem>> &workItems)
|
||||
{
|
||||
bool status {};
|
||||
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
|
||||
for (auto &workItem : workItems)
|
||||
{
|
||||
@ -73,7 +72,7 @@ namespace Aurora::Async
|
||||
}
|
||||
|
||||
dependency->waiters_.push_back(shared_from_this());
|
||||
waitOn_.push_back(workItem);
|
||||
this->waitOn_.push_back(workItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,25 +94,37 @@ namespace Aurora::Async
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::SetSchedTimeNs(AuUInt64 ns)
|
||||
{
|
||||
dispatchTimeNs_ = Time::CurrentClockNS() + ns;
|
||||
this->dispatchTimeNs_ = Time::CurrentClockNS() + ns;
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::SetSchedTimeAbs(AuUInt32 ms)
|
||||
{
|
||||
this->dispatchTimeNs_ = AuUInt64(ms) * AuUInt64(1000000);
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::SetSchedTimeNsAbs(AuUInt64 ns)
|
||||
{
|
||||
this->dispatchTimeNs_ = ns;
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::SetSchedTime(AuUInt32 ms)
|
||||
{
|
||||
dispatchTimeNs_ = Time::CurrentClockNS() + (AuUInt64(ms) * AuUInt64(1000000));
|
||||
this->dispatchTimeNs_ = Time::CurrentClockNS() + (AuUInt64(ms) * AuUInt64(1000000));
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::AddDelayTime(AuUInt32 ms)
|
||||
{
|
||||
delayTimeNs_ += AuUInt64(ms) * AuUInt64(1000000);
|
||||
this->delayTimeNs_ += AuUInt64(ms) * AuUInt64(1000000);
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
AuSPtr<IWorkItem> WorkItem::AddDelayTimeNs(AuUInt64 ns)
|
||||
{
|
||||
delayTimeNs_ += ns;
|
||||
this->delayTimeNs_ += ns;
|
||||
return AU_SHARED_FROM_THIS;
|
||||
}
|
||||
|
||||
@ -129,7 +140,7 @@ namespace Aurora::Async
|
||||
|
||||
if (check)
|
||||
{
|
||||
if (dispatchPending_)
|
||||
if (this->dispatchPending_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -152,9 +163,9 @@ namespace Aurora::Async
|
||||
itr = waitOn_.erase(itr);
|
||||
}
|
||||
|
||||
dispatchPending_ = true;
|
||||
this->dispatchPending_ = true;
|
||||
|
||||
if (Time::CurrentClockNS() < dispatchTimeNs_)
|
||||
if (Time::CurrentClockNS() < this->dispatchTimeNs_)
|
||||
{
|
||||
Schedule();
|
||||
return;
|
||||
@ -162,7 +173,7 @@ namespace Aurora::Async
|
||||
|
||||
if (auto delay = std::exchange(delayTimeNs_, {}))
|
||||
{
|
||||
dispatchTimeNs_ = delay + Time::CurrentClockNS();
|
||||
this->dispatchTimeNs_ = delay + Time::CurrentClockNS();
|
||||
Schedule();
|
||||
return;
|
||||
}
|
||||
@ -170,21 +181,32 @@ namespace Aurora::Async
|
||||
SendOff();
|
||||
}
|
||||
|
||||
float WorkItem::GetPrio()
|
||||
{
|
||||
return prio_;
|
||||
}
|
||||
|
||||
void WorkItem::SetPrio(float val)
|
||||
{
|
||||
prio_ = val;
|
||||
}
|
||||
|
||||
void WorkItem::CancelAsync()
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
Fail();
|
||||
}
|
||||
|
||||
void WorkItem::RunAsync()
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
|
||||
IWorkItemHandler::ProcessInfo info(true);
|
||||
info.pool = this->owner_->ToThreadPool();
|
||||
|
||||
if (task_)
|
||||
if (this->task_)
|
||||
{
|
||||
task_->DispatchFrame(info);
|
||||
this->task_->DispatchFrame(info);
|
||||
}
|
||||
|
||||
switch (info.type)
|
||||
@ -225,13 +247,13 @@ namespace Aurora::Async
|
||||
}
|
||||
}
|
||||
|
||||
finished = true;
|
||||
if (finishedEvent_)
|
||||
this->finished = true;
|
||||
if (this->finishedEvent_)
|
||||
{
|
||||
finishedEvent_->Set();
|
||||
this->finishedEvent_->Set();
|
||||
}
|
||||
|
||||
for (auto &waiter : waiters_)
|
||||
for (auto &waiter : this->waiters_)
|
||||
{
|
||||
std::reinterpret_pointer_cast<WorkItem>(waiter)->DispatchEx(true);
|
||||
}
|
||||
@ -246,59 +268,69 @@ namespace Aurora::Async
|
||||
task_->Shutdown();
|
||||
}
|
||||
|
||||
for (auto &waiter : waiters_)
|
||||
for (auto &waiter : this->waiters_)
|
||||
{
|
||||
std::reinterpret_pointer_cast<WorkItem>(waiter)->Fail();
|
||||
}
|
||||
|
||||
waiters_.clear();
|
||||
waitOn_.clear();
|
||||
this->waiters_.clear();
|
||||
this->waitOn_.clear();
|
||||
|
||||
if (finishedEvent_)
|
||||
if (this->finishedEvent_)
|
||||
{
|
||||
finishedEvent_->Set();
|
||||
this->finishedEvent_->Set();
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkItem::BlockUntilComplete()
|
||||
{
|
||||
if (!finishedEvent_) return false;
|
||||
return static_cast<AsyncApp *>(GetAsyncApp())->WaitFor(this->worker_, AuUnsafeRaiiToShared(finishedEvent_), 0);
|
||||
if (!this->finishedEvent_) return false;
|
||||
return this->owner_->WaitFor(this->worker_, AuUnsafeRaiiToShared(this->finishedEvent_), 0);
|
||||
}
|
||||
|
||||
bool WorkItem::HasFinished()
|
||||
{
|
||||
return finished;
|
||||
return this->finished;
|
||||
}
|
||||
|
||||
void WorkItem::Cancel()
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
Fail();
|
||||
}
|
||||
|
||||
bool WorkItem::HasFailed()
|
||||
{
|
||||
return failed;
|
||||
return this->failed;
|
||||
}
|
||||
|
||||
void WorkItem::Schedule()
|
||||
{
|
||||
Async::Schedule(dispatchTimeNs_, worker_, this->shared_from_this());
|
||||
Async::Schedule(this->dispatchTimeNs_, this->owner_, this->worker_, this->shared_from_this());
|
||||
}
|
||||
|
||||
void WorkItem::SendOff()
|
||||
{
|
||||
if (!task_)
|
||||
if (!this->task_)
|
||||
{
|
||||
// If we aren't actually calling a task interface, we may as well just dispatch objects waiting on us from here
|
||||
RunAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<AsyncApp *>(GetAsyncApp())->Run(worker_, this->shared_from_this());
|
||||
this->owner_->Run(this->worker_, this->shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
static auto GetWorkerInternal(const AuSPtr<IThreadPool> &pool)
|
||||
{
|
||||
return std::static_pointer_cast<ThreadPool>(pool).get();
|
||||
}
|
||||
|
||||
static auto GetWorkerInternal()
|
||||
{
|
||||
return static_cast<AsyncApp *>(GetAsyncApp());
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IWorkItem> NewWorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking)
|
||||
{
|
||||
@ -306,12 +338,28 @@ namespace Aurora::Async
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return AuMakeShared<WorkItem>(worker, task, supportsBlocking);
|
||||
|
||||
return AuMakeShared<WorkItem>(GetWorkerInternal(), worker, task, supportsBlocking);
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IWorkItem> NewWorkItem(const WorkerPId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking)
|
||||
{
|
||||
if (!task)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!worker.pool)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return AuMakeShared<WorkItem>(GetWorkerInternal(), worker, task, supportsBlocking);
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IWorkItem> NewFence()
|
||||
{
|
||||
return AuMakeShared<WorkItem>(WorkerId_t{}, AuSPtr<IWorkItemHandler>{}, true);
|
||||
return AuMakeShared<WorkItem>(GetWorkerInternal(), WorkerId_t{}, AuSPtr<IWorkItemHandler>{}, true);
|
||||
}
|
||||
|
||||
void *WorkItem::GetPrivateData()
|
||||
|
@ -7,12 +7,14 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "ThreadPool.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
class WorkItem : public IWorkItem, public IAsyncRunnable, public std::enable_shared_from_this<WorkItem>
|
||||
{
|
||||
public:
|
||||
WorkItem(const WorkerId_t &worker_, const AuSPtr<IWorkItemHandler> &task_, bool supportsBlocking);
|
||||
WorkItem(IThreadPoolInternal *owner, const WorkerId_t &worker_, const AuSPtr<IWorkItemHandler> &task_, bool supportsBlocking);
|
||||
~WorkItem();
|
||||
|
||||
AuSPtr<IWorkItem> WaitFor(const AuSPtr<IWorkItem> &workItem) override;
|
||||
@ -21,6 +23,8 @@ namespace Aurora::Async
|
||||
AuSPtr<IWorkItem> SetSchedTimeNs(AuUInt64 ns) override;
|
||||
AuSPtr<IWorkItem> AddDelayTime(AuUInt32 ms) override;
|
||||
AuSPtr<IWorkItem> AddDelayTimeNs(AuUInt64 ns) override;
|
||||
AuSPtr<IWorkItem> SetSchedTimeAbs(AuUInt32 ms) override;
|
||||
AuSPtr<IWorkItem> SetSchedTimeNsAbs(AuUInt64 ns) override;
|
||||
AuSPtr<IWorkItem> Then(const AuSPtr<IWorkItem> &next) override;
|
||||
|
||||
AuSPtr<IWorkItem> Dispatch() override;
|
||||
@ -38,10 +42,14 @@ namespace Aurora::Async
|
||||
void *GetPrivateData() override;
|
||||
AuOptional<void *> ToWorkResultT() override;
|
||||
|
||||
float GetPrio() override;
|
||||
void SetPrio(float val) override;
|
||||
|
||||
private:
|
||||
void DispatchEx(bool check);
|
||||
AuSPtr<IWorkItemHandler> task_;
|
||||
WorkerId_t worker_;
|
||||
float prio_ = 0.5f;
|
||||
AuList<AuSPtr<IWorkItem>> waitOn_;
|
||||
AuList<AuSPtr<IWorkItem>> waiters_;
|
||||
AuThreadPrimitives::SpinLock lock;
|
||||
@ -52,10 +60,10 @@ namespace Aurora::Async
|
||||
bool dispatchPending_ {};
|
||||
AuUInt64 dispatchTimeNs_ {};
|
||||
AuUInt64 delayTimeNs_ {};
|
||||
IThreadPoolInternal *owner_ {};
|
||||
|
||||
void Fail();
|
||||
void Schedule();
|
||||
void SendOff();
|
||||
};
|
||||
|
||||
}
|
@ -13,12 +13,12 @@ namespace Aurora::Console::Commands
|
||||
struct Command;
|
||||
struct CommandDispatch;
|
||||
|
||||
static AuHashMap<AuString, Command> gCommands;
|
||||
static AuList<Hooks::LineHook_cb> gLineCallbacks;
|
||||
static AuList<CommandDispatch> gPendingCommands;
|
||||
static auto gMutex = AuThreadPrimitives::MutexUnique();
|
||||
static auto gPendingCommandsMutex = AuThreadPrimitives::MutexUnique();
|
||||
static AuOptional<Async::WorkerId_t> gCommandDispatcher;
|
||||
static AuHashMap<AuString, Command> gCommands;
|
||||
static AuList<Hooks::LineHook_cb> gLineCallbacks;
|
||||
static AuList<CommandDispatch> gPendingCommands;
|
||||
static auto gMutex = AuThreadPrimitives::MutexUnique();
|
||||
static auto gPendingCommandsMutex = AuThreadPrimitives::MutexUnique();
|
||||
static Async::WorkerPId_t gCommandDispatcher;
|
||||
|
||||
struct Command
|
||||
{
|
||||
@ -45,7 +45,7 @@ namespace Aurora::Console::Commands
|
||||
eAsync
|
||||
};
|
||||
|
||||
static bool Dispatch(const AuString &string, EDispatchType type, Async::WorkerId_t workerId)
|
||||
static bool Dispatch(const AuString &string, EDispatchType type, Async::WorkerPId_t workerId)
|
||||
{
|
||||
Parse::ParseResult res;
|
||||
AuSPtr<ICommandSubscriber> callback;
|
||||
@ -105,7 +105,7 @@ namespace Aurora::Console::Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
Async::DispatchBasicWorkCallback<CommandDispatch>(workerId,
|
||||
Async::DispatchWork<CommandDispatch>(workerId,
|
||||
Async::TaskFromConsumerRefT<CommandDispatch>([](const CommandDispatch &dispatch) -> void
|
||||
{
|
||||
dispatch.callback->OnCommand(dispatch.arguments);
|
||||
@ -134,28 +134,15 @@ namespace Aurora::Console::Commands
|
||||
{
|
||||
return Dispatch(string, EDispatchType::eNow, {});
|
||||
}
|
||||
|
||||
|
||||
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerId_t id)
|
||||
{
|
||||
return Dispatch(string, EDispatchType::eAsync, id);
|
||||
return Dispatch(string, EDispatchType::eAsync, AuAsync::WorkerPId_t(AuUnsafeRaiiToShared(AuAsync::GetAsyncApp()), id));
|
||||
}
|
||||
|
||||
static AuSPtr<ITextLineSubscriber> gExternalLineProcessor;
|
||||
|
||||
AUKN_SYM bool DispatchRawLine(const AuString &string)
|
||||
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerPId_t id)
|
||||
{
|
||||
if (gExternalLineProcessor)
|
||||
{
|
||||
gExternalLineProcessor->OnProcessedLineUTF8(string);
|
||||
return true;
|
||||
}
|
||||
|
||||
return DispatchCommand(string);
|
||||
}
|
||||
|
||||
AUKN_SYM void SetCallbackAndDisableCmdProcessing(const AuSPtr<ITextLineSubscriber> &subscriber)
|
||||
{
|
||||
gExternalLineProcessor = subscriber;
|
||||
return Dispatch(string, EDispatchType::eAsync, id);
|
||||
}
|
||||
|
||||
void UpdateDispatcher(AuOptional<Async::WorkerId_t> target)
|
||||
@ -173,7 +160,7 @@ namespace Aurora::Console::Commands
|
||||
}
|
||||
}
|
||||
|
||||
gCommandDispatcher = target;
|
||||
gCommandDispatcher = Async::WorkerPId_t(AuUnsafeRaiiToShared(AuAsync::GetAsyncApp()), target.value());
|
||||
}
|
||||
|
||||
static void DispatchCommandsFromThis(const AuList<CommandDispatch> &commands)
|
||||
@ -192,13 +179,16 @@ namespace Aurora::Console::Commands
|
||||
auto commands = std::exchange(gPendingCommands, {});
|
||||
gPendingCommandsMutex->Unlock();
|
||||
|
||||
if (gCommandDispatcher.value_or(Async::WorkerId_t{}) == Async::WorkerId_t{})
|
||||
if ((gCommandDispatcher.pool == nullptr) ||
|
||||
((gCommandDispatcher.pool.get() == Async::GetAsyncApp()) &&
|
||||
(gCommandDispatcher == Async::WorkerId_t{})))
|
||||
{
|
||||
DispatchCommandsFromThis(commands);
|
||||
}
|
||||
else
|
||||
{
|
||||
Async::NewWorkItem(gCommandDispatcher.value(),
|
||||
|
||||
NewWorkItem(gCommandDispatcher,
|
||||
AuMakeShared<Async::BasicWorkStdFunc>([&commands]()
|
||||
{
|
||||
DispatchCommandsFromThis(commands);
|
||||
|
@ -26,11 +26,22 @@ namespace Aurora::Console
|
||||
return ConsoleStd::ReadStdIn(buffer, length);
|
||||
}
|
||||
|
||||
AUKN_SYM AuUInt32 WriteStdIn(const void *buffer, AuUInt32 length)
|
||||
AUKN_SYM AuUInt32 WriteStdOut(const void *buffer, AuUInt32 length)
|
||||
{
|
||||
return ConsoleStd::WriteStdOut(buffer, length);
|
||||
}
|
||||
|
||||
AUKN_SYM bool DispatchRawLine(const AuString &string)
|
||||
{
|
||||
if (Hooks::gExternalLineProcessor)
|
||||
{
|
||||
Hooks::gExternalLineProcessor->OnProcessedLineUTF8(string);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Commands::DispatchCommand(string);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
Hooks::Init();
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
namespace Aurora::Console::ConsoleFIO
|
||||
{
|
||||
static AuList<AuUInt8> gLogBuffer;
|
||||
static AuList<AuUInt8> gLogBuffer;
|
||||
static AuThreadPrimitives::RWLockUnique_t gLogMutex;
|
||||
static IO::FS::OpenWriteUnique_t gFileHandle;
|
||||
static AuIOFS::OpenWriteUnique_t gFileHandle;
|
||||
|
||||
static const auto &gLogConfig = gRuntimeConfig.console.fio;
|
||||
|
||||
@ -21,7 +21,7 @@ namespace Aurora::Console::ConsoleFIO
|
||||
AuString path;
|
||||
AuString procName;
|
||||
|
||||
if (!IO::FS::GetProfileDomain(path))
|
||||
if ((gLogConfig.writeLogsToUserDir) || (!IO::FS::GetProfileDomain(path)))
|
||||
{
|
||||
path = ".";
|
||||
}
|
||||
|
@ -32,16 +32,16 @@ namespace Aurora::Console::ConsoleStd
|
||||
#if defined(ENABLE_STD_CONSOLE)
|
||||
|
||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||
using StreamHandle_t = HANDLE;
|
||||
#define IS_STREAM_HANDLE_VALID(h) (h != INVALID_HANDLE_VALUE)
|
||||
#define DEFAULT_HANDLE_VAL INVALID_HANDLE_VALUE
|
||||
|
||||
using StreamHandle_t = HANDLE;
|
||||
|
||||
static StreamHandle_t gWin32Thread = INVALID_HANDLE_VALUE;
|
||||
#elif defined(IO_POSIX_STREAMS)
|
||||
#define IS_STREAM_HANDLE_VALID(h) (h != 0)
|
||||
#define DEFAULT_HANDLE_VAL 0
|
||||
#define DEFAULT_HANDLE_VAL 0xFFFFFFFF
|
||||
using StreamHandle_t = int;
|
||||
#endif
|
||||
#endif
|
||||
#define IS_STREAM_HANDLE_VALID(h) (h != DEFAULT_HANDLE_VAL)
|
||||
|
||||
static bool AsyncReadAnyOrReadStreamBlock();
|
||||
|
||||
static const AuMach kLineBufferMax = 2048;
|
||||
@ -229,12 +229,12 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
if (GetConsoleWindow() == NULL)
|
||||
{
|
||||
if (!gRuntimeConfig.console.forceConsoleWindow)
|
||||
if (!gRuntimeConfig.console.enableConsole)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gRuntimeConfig.console.disableAllConsoles)
|
||||
|
||||
if (!gRuntimeConfig.console.forceConsoleWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -353,7 +353,7 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
if (line.size())
|
||||
{
|
||||
Console::Commands::DispatchRawLine(line);
|
||||
Console::DispatchRawLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ void ConsoleFrame::OnCmd(wxCommandEvent &event)
|
||||
|
||||
textbox->Clear();
|
||||
|
||||
Aurora::Console::Commands::DispatchRawLine(line);
|
||||
Aurora::Console::DispatchRawLine(line);
|
||||
}
|
||||
|
||||
WxSplitterLine *ConsoleFrame::NewSplitter(wxSize splitter, wxColor color)
|
||||
@ -266,6 +266,8 @@ ConsoleFrame::ConsoleFrame(const wxString &title, const wxPoint &pos, const wxSi
|
||||
// \_/\_/ \__,_|_| \___|_| |_|_| |_| |_|\___||___/ \__,_|_| |_|\___|\__,_|\__,_| (_) (_) (_) (_)
|
||||
//
|
||||
//
|
||||
// I dont even care if this function leaks on failure. sucks to be you, i dont care.
|
||||
// valgrind can fail when your system is running low on resources at start up
|
||||
|
||||
|
||||
static const auto kEnableDark = (useWin32DarkHack) ||
|
||||
@ -637,7 +639,7 @@ namespace Aurora::Console::ConsoleWxWidgets
|
||||
|
||||
static bool UseWxConsole()
|
||||
{
|
||||
if (gRuntimeConfig.console.disableAllConsoles)
|
||||
if (!gRuntimeConfig.console.enableConsole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -722,7 +724,7 @@ namespace Aurora::Console::ConsoleWxWidgets
|
||||
|
||||
}
|
||||
|
||||
static Aurora::Threading::Threads::ThreadUnique_t gWxWidgetsThread;
|
||||
static AuThreads::ThreadUnique_t gWxWidgetsThread;
|
||||
|
||||
static void WxWidgetsThreadMain()
|
||||
{
|
||||
@ -770,18 +772,17 @@ namespace Aurora::Console::ConsoleWxWidgets
|
||||
if (std::exchange(gConsoleStarted, true)) return;
|
||||
|
||||
gMutex = AuThreadPrimitives::MutexUnique();
|
||||
if (!gMutex) return;
|
||||
|
||||
Aurora::Console::Hooks::AddSubscription(AuUnsafeRaiiToShared(&gConsoleMessageSubscriber));
|
||||
|
||||
gWxWidgetsThread = AuThreading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo(
|
||||
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(WxWidgetsThreadMain)),
|
||||
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}),
|
||||
gWxWidgetsThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
|
||||
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(WxWidgetsThreadMain)),
|
||||
AuThreads::IThreadVectorsFunctional::OnExit_t{}),
|
||||
"WxWidgets"
|
||||
));
|
||||
if (!gWxWidgetsThread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!gWxWidgetsThread) return;
|
||||
|
||||
gWxWidgetsThread->Run();
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,9 @@
|
||||
|
||||
namespace Aurora::Console
|
||||
{
|
||||
static Threading::Threads::ThreadUnique_t gWriterThread;
|
||||
static AuThreads::ThreadUnique_t gWriterThread;
|
||||
|
||||
class ShutdownFlushHook : public Threading::Threads::IThreadFeature
|
||||
class ShutdownFlushHook : public AuThreads::IThreadFeature
|
||||
{
|
||||
public:
|
||||
void Init() override;
|
||||
@ -38,7 +38,7 @@ namespace Aurora::Console
|
||||
|
||||
static void LogThreadEP()
|
||||
{
|
||||
auto thread = Threading::Threads::GetThread();
|
||||
auto thread = AuThreads::GetThread();
|
||||
|
||||
SlowStartupTasks();
|
||||
|
||||
@ -58,9 +58,9 @@ namespace Aurora::Console
|
||||
{
|
||||
// Startup a runner thread that will take care of all the stress inducing IO every so often on a remote thread
|
||||
|
||||
gWriterThread = Threading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo(
|
||||
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(LogThreadEP)),
|
||||
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}),
|
||||
gWriterThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
|
||||
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(LogThreadEP)),
|
||||
AuThreads::IThreadVectorsFunctional::OnExit_t{}),
|
||||
"CasualConsoleAsyncWritter"
|
||||
));
|
||||
if (!gWriterThread)
|
||||
@ -73,7 +73,7 @@ namespace Aurora::Console
|
||||
void InitFlusher()
|
||||
{
|
||||
// Add a 'ShutdownFlushHook' object to the main threads TLS hook
|
||||
Threading::Threads::GetThread()->AddLastHopeTlsHook(AuMakeShared<ShutdownFlushHook>());
|
||||
AuThreads::GetThread()->AddLastHopeTlsHook(AuMakeShared<ShutdownFlushHook>());
|
||||
|
||||
InitFlushThread();
|
||||
}
|
||||
|
@ -32,11 +32,16 @@ namespace Aurora::Console::Hooks
|
||||
AuTryInsert(gLineFunctionalCallbacks, hook);
|
||||
}
|
||||
|
||||
AUKN_SYM void SetCallbackAndDisableCmdProcessing(const AuSPtr<Hooks::ITextLineSubscriber> &subscriber)
|
||||
{
|
||||
gExternalLineProcessor = subscriber;
|
||||
}
|
||||
|
||||
void WriteLine(const ConsoleMessage &msg)
|
||||
{
|
||||
AU_LOCK_GUARD(gMutex);
|
||||
|
||||
if (msg.line.find('\n') == std::string::npos) [[likely]]
|
||||
if (msg.line.find('\n') == AuString::npos) [[likely]]
|
||||
{
|
||||
for (const auto &callback : gLineFunctionalCallbacks)
|
||||
{
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
namespace Aurora::Console::Hooks
|
||||
{
|
||||
inline AuSPtr<Hooks::ITextLineSubscriber> gExternalLineProcessor;
|
||||
|
||||
void Deinit();
|
||||
void Init();
|
||||
void WriteLine(const ConsoleMessage &msg);
|
||||
|
@ -91,9 +91,15 @@ namespace Aurora::Debug
|
||||
}
|
||||
}
|
||||
|
||||
if (SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, pSymbol))
|
||||
try
|
||||
{
|
||||
//if (SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, pSymbol))
|
||||
{
|
||||
//frameCurrent.label = pSymbol->Name;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
frameCurrent.label = pSymbol->Name;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(STAGING)
|
||||
@ -193,6 +199,7 @@ namespace Aurora::Debug
|
||||
SymInitialize(GetCurrentProcess(), NULL, TRUE);
|
||||
#endif
|
||||
|
||||
// This is really gross :(
|
||||
AddVectoredExceptionHandler(1,
|
||||
[](_EXCEPTION_POINTERS *ExceptionInfo) -> LONG
|
||||
{
|
||||
@ -240,14 +247,13 @@ namespace Aurora::Debug
|
||||
const auto catchableTypeArray = reinterpret_cast<const CatchableTypeArray*>(reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(throwInfo->pCatchableTypeArray));
|
||||
|
||||
AuString suffix;
|
||||
bool derivedFromException = {};
|
||||
|
||||
for (int i = 0; i < catchableTypeArray->nCatchableTypes; i++)
|
||||
{
|
||||
const auto type = reinterpret_cast<CatchableType *> (reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(catchableTypeArray->arrayOfCatchableTypes[i]));
|
||||
const auto descriptor = reinterpret_cast<std::type_info *> (reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(type->pType));
|
||||
|
||||
entry.wincxx.str += (i == 0 ? "" : AuString(", ")) + descriptor->name();
|
||||
entry.wincxx.str += (i == 0 ? "" : AuString(", ")) + descriptor->name(); // __std_type_info_name
|
||||
|
||||
if (strnicmp(descriptor->raw_name(), ".?AVException@", AuArraySize(".?AVException@") - 1) == 0)
|
||||
{
|
||||
@ -258,7 +264,7 @@ namespace Aurora::Debug
|
||||
suffix = wptr;
|
||||
}
|
||||
}
|
||||
else if (strncmp(descriptor->raw_name(), ".PEAD", AuArraySize(".PEAD") - 1) == 0)
|
||||
else if (strncmp(descriptor->raw_name(), ".PEAD", AuArraySize(".PEAD")) == 0)
|
||||
{
|
||||
auto possibleStringPointer = reinterpret_cast<const char **>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
|
||||
if (IsReadable(possibleStringPointer))
|
||||
@ -272,7 +278,7 @@ namespace Aurora::Debug
|
||||
}
|
||||
else if (strncmp(descriptor->raw_name(), kStringRawName.data(), kStringRawName.size()) == 0)
|
||||
{
|
||||
auto possibleStdStringPointer = reinterpret_cast<std::string *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
|
||||
auto possibleStdStringPointer = reinterpret_cast<AuString *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
|
||||
if (IsReadable(possibleStdStringPointer))
|
||||
{
|
||||
auto string = *possibleStdStringPointer;
|
||||
|
@ -55,10 +55,82 @@ static void Deinit()
|
||||
Aurora::Processes::Deinit();
|
||||
}
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
|
||||
template<typename ReturnValue_t = void, typename ... Args, class Clazz_t, class FunctorTask_t, class FuckYou_t >
|
||||
static AuSPtr<IWorkItem> DispathSmartWork(const WorkerId_t &worker, AuSPtr<Clazz_t> owner, FunctorTask_t task, FuckYou_t job, Args ... in)
|
||||
{
|
||||
return DispatchWork<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t>(worker,
|
||||
TaskFromTupleCallableWithBindOwner2<FTask<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t>, ReturnValue_t, FunctorTask_t>(task),
|
||||
Async::JobFromTupleClazz<ReturnValue_t, AuSPtr<Clazz_t>, Args...>(job),
|
||||
std::make_tuple<AuSPtr<Clazz_t>, Args...>(AU_FWD(owner), std::forward<Args>(in)...),
|
||||
false);
|
||||
}
|
||||
|
||||
template<typename ReturnValue_t = void, typename ... Args, class Clazz_t, class FunctorTask_t, class FuckYou_t, class FuckYou2_t >
|
||||
static AuSPtr<IWorkItem> DispathSmartWorkEx(const WorkerId_t &worker, AuSPtr<Clazz_t> owner, FunctorTask_t task, FuckYou_t success, FuckYou2_t failure, Args ... in)
|
||||
{
|
||||
return DispatchWork<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t>(worker,
|
||||
TaskFromTupleCallableWithBindOwner2<FTask<std::tuple<AuSPtr<Clazz_t>, Args...>, ReturnValue_t>, ReturnValue_t, FunctorTask_t>(task),
|
||||
Async::JobFromTupleClazzEx<ReturnValue_t, AuSPtr<Clazz_t>, Args...>(success, failure),
|
||||
std::make_tuple<AuSPtr<Clazz_t>, Args...>(AU_FWD(owner), std::forward<Args>(in)...),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Aurora
|
||||
{
|
||||
static bool gRuntimeHasStarted {};
|
||||
|
||||
struct A : std::enable_shared_from_this<A>
|
||||
{
|
||||
AuUInt64 DoWork(AuUInt64 in, AuUInt64 in2)
|
||||
{
|
||||
return in * 2 * in2;
|
||||
}
|
||||
|
||||
void DoWorkComplete(const AuSPtr<A> &worker, const AuUInt64 &out)
|
||||
{
|
||||
AuLog::LogVerbose("Math calculation finished on a remote thread, result: {}", out);
|
||||
}
|
||||
|
||||
static void DoWorkFail(const AuSPtr<A> &worker)
|
||||
{
|
||||
SysPanic("How did I fail?");
|
||||
}
|
||||
|
||||
|
||||
void DoWorkComplete2(const AuUInt64 &a, const AuUInt64 &b, const AuUInt64 &c)
|
||||
{
|
||||
AuLog::LogVerbose("Math calculation finished on a remote thread, result: {} * 2 * {} = {}", a, b, c);
|
||||
}
|
||||
|
||||
void DoWorkFail2(AuUInt64 a, AuUInt64 b)
|
||||
{
|
||||
SysPanic("How did I fail?");
|
||||
}
|
||||
|
||||
void RunAsyncMath(AuUInt64 in, AuUInt64 in2)
|
||||
{
|
||||
Async::DispathSmartWorkEx<AuUInt64, AuUInt64, AuUInt64>(AuWorkerId_t {},
|
||||
AuSharedFromThis(),
|
||||
std::bind(&A::DoWork, AuSharedFromThis(), std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind<void>(&A::DoWorkComplete, AuSharedFromThis(), std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind<void>(DoWorkFail, std::placeholders::_1),
|
||||
in,
|
||||
in2);
|
||||
|
||||
Async::DispatchWork<std::tuple<AuUInt64, AuUInt64>,AuUInt64>(AuWorkerId_t {},
|
||||
AuAsync::TaskFromTupleCallable<AuUInt64, AuUInt64, AuUInt64>(std::bind(&A::DoWork, AuSharedFromThis(), std::placeholders::_1, std::placeholders::_2)),
|
||||
AuAsync::JobFromTupleConsumerEx<AuUInt64, AuUInt64, AuUInt64>(
|
||||
std::bind<void>(&A::DoWorkComplete2, AuSharedFromThis(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
|
||||
std::bind<void>(&A::DoWorkFail2, AuSharedFromThis(), std::placeholders::_1, std::placeholders::_2)),
|
||||
std::make_tuple(in, in2));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
AUKN_SYM void RuntimeStart(const RuntimeStartInfo &info)
|
||||
{
|
||||
gRuntimeConfig = info;
|
||||
|
@ -41,27 +41,27 @@ namespace Aurora::HWInfo
|
||||
|
||||
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
|
||||
#if defined(AURORA_COMPILER_MSVC)
|
||||
static inline CPUIdContext cpuid(AuUInt32 a)
|
||||
static CPUIdContext cpuid(AuUInt32 a)
|
||||
{
|
||||
CPUIdContext context;
|
||||
__cpuid(context.regs, a);
|
||||
return context;
|
||||
}
|
||||
#elif defined(AURORA_COMPILER_CLANG) || defined(AURORA_COMPILER_GCC)
|
||||
static inline CPUIdContext cpuid(AuUInt32 a)
|
||||
static CPUIdContext cpuid(AuUInt32 a)
|
||||
{
|
||||
CPUIdContext context;
|
||||
__get_cpuid(a, &context.eax, &context.ebx, &context.ecx, &context.edx);
|
||||
return context;
|
||||
}
|
||||
#else
|
||||
static inline CPUIdContext cpuid(AuUInt32 a)
|
||||
static CPUIdContext cpuid(AuUInt32 a)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static inline CPUIdContext cpuid(AuUInt32 a)
|
||||
static CPUIdContext cpuid(AuUInt32 a)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ namespace Aurora::IO::FS
|
||||
return;
|
||||
}
|
||||
|
||||
auto hold = std::exchange(this->pin_, {}); // this has side effects
|
||||
auto hold = std::exchange(this->pin_, {});
|
||||
|
||||
if (hold->sub_)
|
||||
{
|
||||
|
@ -146,6 +146,24 @@ namespace Aurora::IO::FS
|
||||
}
|
||||
}
|
||||
|
||||
static void SetXdg(AuString &out, const char *envvar, const char *home, const char *defaultHomeExt, const char *fallback)
|
||||
{
|
||||
auto value = getenv(envvar);
|
||||
|
||||
if (value)
|
||||
{
|
||||
out = value;
|
||||
}
|
||||
else if (home)
|
||||
{
|
||||
out = AuString(home) + defaultHomeExt;
|
||||
}
|
||||
else
|
||||
{
|
||||
out = fallback;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetNamespaceDirectories()
|
||||
{
|
||||
const char *homedir;
|
||||
@ -156,22 +174,12 @@ namespace Aurora::IO::FS
|
||||
homedir = getpwuid(getuid())->pw_dir;
|
||||
}
|
||||
|
||||
gHomeDirectory = homedir ? homedir : "";
|
||||
// XDG Base Directory Specification
|
||||
// $XDG_CONFIG_HOME defines the base directory relative to which user-specific configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
|
||||
// $XDG_DATA_HOME defines the base directory relative to which user-specific data files should be stored
|
||||
SetXdg(gApplicationData, "XDG_CONFIG_HOME", homedir, "/.config", kUnixAppData);
|
||||
SetXdg(gHomeDirectory, "XDG_DATA_HOME", homedir, "/.local/share", ".");
|
||||
|
||||
if (gHomeDirectory.empty())
|
||||
{
|
||||
gHomeDirectory = ".";
|
||||
}
|
||||
|
||||
if (FS::DirExists(kUnixAppData))
|
||||
{
|
||||
gApplicationData = kUnixAppData;
|
||||
}
|
||||
else
|
||||
{
|
||||
gApplicationData = gHomeDirectory;
|
||||
}
|
||||
|
||||
SetUnixPaths(gUserLibPath, gUserLibPath2, "/usr/lib");
|
||||
SetUnixPaths(gSystemLibPath, gSystemLibPath2, "/lib");
|
||||
}
|
||||
@ -209,23 +217,23 @@ namespace Aurora::IO::FS
|
||||
static void ChangeDir()
|
||||
{
|
||||
#if !defined(AU_NO_AU_HOME_BRANDING)
|
||||
if (gRuntimeConfig.fio.defaultBrand.has_value())
|
||||
if (gRuntimeConfig.fio.defaultBrand.size())
|
||||
{
|
||||
gApplicationData += "/" + gRuntimeConfig.fio.defaultBrand.value() + "/System";
|
||||
#if defined(AURORA_IS_POSIX_DERIVED)
|
||||
gHomeDirectory += "/." + gRuntimeConfig.fio.defaultBrand.value() + "/Profile";
|
||||
#else
|
||||
gHomeDirectory += "/" + gRuntimeConfig.fio.defaultBrand.value() + "/Profile";
|
||||
#endif
|
||||
gApplicationData += "/" + gRuntimeConfig.fio.defaultBrand;
|
||||
gHomeDirectory += "/" + gRuntimeConfig.fio.defaultBrand;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
gHomeDirectory += "/.config"; //most unix programs hide their private user data under here
|
||||
}
|
||||
|
||||
NormalizePath(gApplicationData);
|
||||
NormalizePath(gHomeDirectory);
|
||||
|
||||
if (gApplicationData == gHomeDirectory)
|
||||
{
|
||||
gApplicationData += kPathSplitter;
|
||||
gHomeDirectory += kPathSplitter;
|
||||
gApplicationData += "System";
|
||||
gHomeDirectory += "Profile";
|
||||
}
|
||||
|
||||
// Noting we append a path splitter to prevent hair pulling over missing path delimiters
|
||||
// Eg: GetHome() + "myAwesomeApp/Config" = %HOME%/Aurora/ProfilemyAwsomeApp/Config
|
||||
|
@ -2,6 +2,8 @@
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Net.cpp
|
||||
Date: 2021-6-17
|
||||
Date: 2021-7-2
|
||||
Author: Reece
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "Net.hpp"
|
@ -2,6 +2,9 @@
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: Net.hpp
|
||||
Date: 2021-6-17
|
||||
Date: 2021-7-2
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "SocketStatChannel.hpp"
|
86
Source/IO/Net/SocketStatAverageBps.hpp
Normal file
86
Source/IO/Net/SocketStatAverageBps.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: SocketStatAverageBps.hpp
|
||||
Date: 2021-10-31
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::IO::Net
|
||||
{
|
||||
struct SocketStatAverageBps
|
||||
{
|
||||
inline AuUInt32 GetBytesPerMS(AuUInt32 timeNow)
|
||||
{
|
||||
return (double(bytesTransferred) / (double(frameStart - timeNow) + std::numeric_limits<double>::epsilon()));
|
||||
}
|
||||
|
||||
inline AuUInt32 GetBytesPerSecondNormalized(AuUInt32 timeNow)
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
|
||||
// If timeNow isn't at least that of one socket frame, then we just have to return some old data
|
||||
if (frameZero) return GetLastBytesPerSecond();
|
||||
|
||||
// Cool we can extrapolate usage using a time weight at least that of one server frame
|
||||
|
||||
// Some useful variables...
|
||||
auto frameAtPoint = GetBytesPerMS(timeNow);
|
||||
auto timeElapsed = frameStart - timeNow;
|
||||
|
||||
// Edge case: we aren't receiving much data. if we have more than 1 second of data, we should just average it
|
||||
if (timeElapsed > 1000) return frameAtPoint / 1000; // from ms
|
||||
// else assume constant usage will continue to trend for at least another second
|
||||
// the actual extrapolation
|
||||
auto weight = double(1000) / double(timeElapsed);
|
||||
return double(frameAtPoint) * weight;
|
||||
}
|
||||
|
||||
inline AuUInt32 GetLastBytesPerSecond()
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
return (double(lastBytesTransferred) / (double(frameLastEnd - frameLastStart) + std::numeric_limits<double>::epsilon())) / 1000;
|
||||
}
|
||||
|
||||
inline void Reset()
|
||||
{
|
||||
bytesTransferred = 0;
|
||||
frameStart = 0;
|
||||
frameLastEnd = 0;
|
||||
frameLastStart = 0;
|
||||
lastBytesTransferred = 0;
|
||||
}
|
||||
|
||||
inline void Add(AuUInt32 timeNow, AuUInt32 bytes)
|
||||
{
|
||||
AU_LOCK_GUARD(lock);
|
||||
auto nextTick = frameLastEnd + 1000;
|
||||
|
||||
if (nextTick < timeNow)
|
||||
{
|
||||
frameLastEnd = timeNow;
|
||||
frameLastStart = std::exchange(frameStart, timeNow);
|
||||
lastBytesTransferred = std::exchange(bytesTransferred, bytes);
|
||||
frameZero = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
frameZero = false;
|
||||
bytesTransferred += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AuThreadPrimitives::SpinLock lock;
|
||||
|
||||
AuUInt32 frameStart {};
|
||||
AuUInt32 bytesTransferred {};
|
||||
|
||||
AuUInt32 frameLastStart {};
|
||||
AuUInt32 frameLastEnd {};
|
||||
AuUInt32 lastBytesTransferred {};
|
||||
|
||||
bool frameZero {true};
|
||||
};
|
||||
}
|
36
Source/IO/Net/SocketStatChannel.hpp
Normal file
36
Source/IO/Net/SocketStatChannel.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: SocketStatChannel.hpp
|
||||
Date: 2021-10-31
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "SocketStatAverageBps.hpp"
|
||||
|
||||
namespace Aurora::IO::Net
|
||||
{
|
||||
struct SocketStatChannel
|
||||
{
|
||||
SocketStatAverageBps bandwidth;
|
||||
AuUInt64 total;
|
||||
|
||||
// Interpolated bytes per second, may be less than expected (returning frame -1)
|
||||
inline AuUInt32 GetAverageBytesPerSecond()
|
||||
{
|
||||
return bandwidth.GetLastBytesPerSecond();
|
||||
}
|
||||
|
||||
inline AuUInt32 GetBytesPerSecond()
|
||||
{
|
||||
return bandwidth.GetBytesPerSecondNormalized(Time::CurrentClockMS());
|
||||
}
|
||||
|
||||
inline void Add(AuUInt32 bytes)
|
||||
{
|
||||
total += bytes;
|
||||
bandwidth.Add(Time::CurrentClockMS(), bytes);
|
||||
}
|
||||
};
|
||||
}
|
@ -255,11 +255,6 @@ namespace Aurora::Locale::Encoding
|
||||
return ret;
|
||||
}
|
||||
|
||||
AuStreamReadWrittenPair_t STLCPToUTF8(ECodePage page, void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max)
|
||||
{
|
||||
return DecodeUTF8Internal(in, length, utf8, utf8Max, page);
|
||||
}
|
||||
|
||||
AuStreamReadWrittenPair_t STLCPToUTF8(ECodePage page, const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max)
|
||||
{
|
||||
return DecodeUTF8Internal(in, length, utf8, utf8Max, page);
|
||||
|
@ -19,5 +19,4 @@ namespace Aurora::Locale::Encoding
|
||||
|
||||
AuStreamReadWrittenPair_t STLCPToUTF8(ECodePage page, const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max);
|
||||
AuStreamReadWrittenPair_t STLUTF8ToCp(ECodePage page, const void *utf8, AuUInt32 utf8Length, void *cp, AuUInt32 cpLen);
|
||||
AuStreamReadWrittenPair_t STLCPToUTF8(ECodePage page, void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max);
|
||||
}
|
@ -31,41 +31,13 @@ namespace Aurora::Locale::Encoding
|
||||
this->decode = decode;
|
||||
}
|
||||
|
||||
AuStreamReadWrittenPair_t EncoderAdapter::CPToUTF8(void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max)
|
||||
{
|
||||
AuStreamReadWrittenPair_t ret {};
|
||||
|
||||
if (!length)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!in)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (((page == ECodePage::eSysUnk) &&
|
||||
(GetInternalCodePage() == ECodePage::eUTF8)) ||
|
||||
(page == ECodePage::eUTF8))
|
||||
{
|
||||
auto readable = std::min(length, utf8Max);
|
||||
if (utf8 && in)
|
||||
{
|
||||
std::memcpy(utf8, in, readable);
|
||||
}
|
||||
return {length, utf8 ? utf8Max : length};
|
||||
}
|
||||
|
||||
ret = Win32CPToUTF8(page, in, length, utf8, utf8Max);
|
||||
if (!ret.first)
|
||||
{
|
||||
ret = STLCPToUTF8(page, in, length, utf8, utf8Max);
|
||||
}
|
||||
|
||||
return ret;
|
||||
bool EncoderAdapter::TestPage(ECodePage ref)
|
||||
{
|
||||
return (((page == ECodePage::eSysUnk) &&
|
||||
(GetInternalCodePage() == ref)) ||
|
||||
(page == ref));
|
||||
}
|
||||
|
||||
|
||||
AuStreamReadWrittenPair_t EncoderAdapter::CPToUTF8(const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max)
|
||||
{
|
||||
AuStreamReadWrittenPair_t ret {};
|
||||
@ -80,21 +52,42 @@ namespace Aurora::Locale::Encoding
|
||||
return {};
|
||||
}
|
||||
|
||||
if (((page == ECodePage::eSysUnk) &&
|
||||
(GetInternalCodePage() == ECodePage::eUTF8)) ||
|
||||
(page == ECodePage::eUTF8))
|
||||
// decode using internal and/or optimized apis first
|
||||
if (TestPage(ECodePage::eUTF8))
|
||||
{
|
||||
length = CountUTF8Length({in, length}, true);
|
||||
|
||||
auto readable = std::min(length, utf8Max);
|
||||
if (utf8 && in)
|
||||
{
|
||||
std::memcpy(utf8, in, readable);
|
||||
}
|
||||
return {length, utf8 ? utf8Max : length};
|
||||
|
||||
return {readable, readable};
|
||||
}
|
||||
|
||||
// never remove me. the stl and windows can't bet trusted to fail on conversion failure
|
||||
if (TestPage(ECodePage::eGBK))
|
||||
{
|
||||
length = GBK::CountGbk(in, length, true);
|
||||
}
|
||||
else if (TestPage(ECodePage::eSJIS))
|
||||
{
|
||||
length = SJIS::CountSJIS(in, length, true);
|
||||
}
|
||||
else if (TestPage(ECodePage::eUTF16) || TestPage(ECodePage::eUTF16BE))
|
||||
{
|
||||
length = UTF16::Count16(in, length, true);
|
||||
}
|
||||
else if ((page == ECodePage::eUTF32) || (page == ECodePage::eUTF32BE))
|
||||
{
|
||||
length &= ~3;
|
||||
}
|
||||
|
||||
ret = Win32CPToUTF8(page, in, length, utf8, utf8Max);
|
||||
if (!ret.first)
|
||||
{
|
||||
// TODO: iconv support here
|
||||
ret = STLCPToUTF8(page, in, length, utf8, utf8Max);
|
||||
}
|
||||
|
||||
@ -115,9 +108,7 @@ namespace Aurora::Locale::Encoding
|
||||
return {};
|
||||
}
|
||||
|
||||
if (((page == ECodePage::eSysUnk) &&
|
||||
(GetInternalCodePage() == ECodePage::eUTF8)) ||
|
||||
(page == ECodePage::eUTF8))
|
||||
if (TestPage(ECodePage::eUTF8))
|
||||
{
|
||||
auto readable = std::min(utf8Length, cpLen);
|
||||
if (utf8 && cp)
|
||||
@ -130,6 +121,7 @@ namespace Aurora::Locale::Encoding
|
||||
ret = Win32UTF8ToCp(page, utf8, utf8Length, cp, cpLen);
|
||||
if (!ret.first)
|
||||
{
|
||||
// TODO: iconv support here
|
||||
ret = STLUTF8ToCp(page, utf8, utf8Length, cp, cpLen);
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ namespace Aurora::Locale::Encoding
|
||||
~EncoderAdapter();
|
||||
|
||||
void Init(ECodePage page, bool decode);
|
||||
|
||||
AuStreamReadWrittenPair_t CPToUTF8(void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Len);
|
||||
bool TestPage(ECodePage page);
|
||||
|
||||
AuStreamReadWrittenPair_t CPToUTF8(const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Len);
|
||||
AuStreamReadWrittenPair_t UTF8ToCp(const void *utf8, AuUInt32 utf8Length, void *cp, AuUInt32 cpLen);
|
||||
};
|
||||
|
@ -146,48 +146,6 @@ namespace Aurora::Locale::Encoding
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(reece): Consider implementing bigendian when I can be bothered
|
||||
|
||||
AuStreamReadWrittenPair_t Win32CPToUTF8(ECodePage page, void *in, AuUInt length, void *utf8, AuUInt32 utf8Max)
|
||||
{
|
||||
AuStreamReadWrittenPair_t ret {};
|
||||
|
||||
#if defined(AU_HAS_MSFT_NATIONALLANGSUPPORT)
|
||||
switch (page)
|
||||
{
|
||||
default:
|
||||
case ECodePage::eUnsupported:
|
||||
return {};
|
||||
case ECodePage::e18030:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_CHINESE, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::eSysUnk:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_ACP, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::eLatin1:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_LATIN_1, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::eUTF7:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_UTF7, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::e2312:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_2312_LIMITED_GBK, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::eGBK:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_2312_LIMITED_GBK, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::eSJIS:
|
||||
ret = Win32ConvertFromCPToUTF8(CP_SHIFTJIS, in, length, utf8, utf8Max);
|
||||
break;
|
||||
case ECodePage::eUTF16:
|
||||
ret = Win32ConvertFromUTF16ToUTF8(in, length, utf8, utf8Max);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AuStreamReadWrittenPair_t Win32CPToUTF8(ECodePage page, const void *in, AuUInt length, void *utf8, AuUInt32 utf8Max)
|
||||
{
|
||||
AuStreamReadWrittenPair_t ret {};
|
||||
|
@ -14,7 +14,6 @@ namespace Aurora::Locale::Encoding
|
||||
AuStreamReadWrittenPair_t Win32ConvertFromUTF8ToCp(AuUInt32 cp, const void *utf8, AuUInt utf8Length, void *cpBlob, AuUInt32 cpLen);
|
||||
AuStreamReadWrittenPair_t Win32ConvertFromCPToUTF8(AuUInt32 cp, const void *in, AuUInt length, void *utf8, AuUInt32 utf8Len);
|
||||
|
||||
AuStreamReadWrittenPair_t Win32CPToUTF8(ECodePage page, void *in, AuUInt length, void *utf8, AuUInt32 utf8Max);
|
||||
AuStreamReadWrittenPair_t Win32CPToUTF8(ECodePage page, const void *in, AuUInt length, void *utf8, AuUInt32 utf8Max);
|
||||
AuStreamReadWrittenPair_t Win32UTF8ToCp(ECodePage page, const void *utf8, AuUInt32 utf8Length, void *cp, AuUInt32 cpLen);
|
||||
}
|
@ -11,10 +11,10 @@
|
||||
|
||||
namespace Aurora::Locale::Encoding
|
||||
{
|
||||
AUKN_SYM AuOptional<AuPair<ECodePage, AuUInt8>> DecodeBOM(const Memory::MemoryViewRead & binary)
|
||||
AUKN_SYM BOM DecodeBOM(const Memory::MemoryViewRead & binary)
|
||||
{
|
||||
#define ADD_PATTERN(str, code) {str, AuArraySize(str) - 1, ECodePage::code}
|
||||
AuList<std::tuple<const char *, int, ECodePage>> bows =
|
||||
#define ADD_PATTERN(str, code) {str, {ECodePage::code, AuArraySize(str) - 1}}
|
||||
AuList<std::tuple<const char *, BOM>> bows =
|
||||
{
|
||||
ADD_PATTERN("\xFF\xFE\x00\x00", eUTF32),
|
||||
ADD_PATTERN("\x00\x00\xFE\xFF", eUTF32BE),
|
||||
@ -30,30 +30,32 @@ namespace Aurora::Locale::Encoding
|
||||
};
|
||||
#undef ADD_PATTERN
|
||||
|
||||
for (const auto &[string, length, category] : bows)
|
||||
for (const auto &[string, bom] : bows)
|
||||
{
|
||||
if (binary.length < length) continue;
|
||||
if (std::memcmp(binary.ptr, string, length) != 0) continue;
|
||||
if (binary.length < bom.length) continue;
|
||||
if (std::memcmp(binary.ptr, string, bom.length) != 0) continue;
|
||||
|
||||
return AuMakePair(category, length);
|
||||
return bom;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
AUKN_SYM AuStreamReadWrittenPair_t EncodeUTF8(const Memory::MemoryViewRead & utf8, const Memory::MemoryViewWrite & binary, ECodePage page)
|
||||
// OLD SHIT API
|
||||
|
||||
AUKN_SYM AuStreamReadWrittenPair_t EncodeUTF8(const Memory::MemoryViewRead &utf8, const Memory::MemoryViewWrite & binary, ECodePage page)
|
||||
{
|
||||
TextStreamEncoder re(page);
|
||||
return re.DecodeUTF8(utf8.ptr, utf8.length, binary.ptr, binary.length);
|
||||
}
|
||||
|
||||
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead & binary, const Memory::MemoryViewWrite & utf8, ECodePage page)
|
||||
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead &binary, const Memory::MemoryViewWrite & utf8, ECodePage page)
|
||||
{
|
||||
TextStreamProcessor re(page);
|
||||
return re.EncodeUTF8(binary.ptr, binary.length, utf8.ptr, utf8.length);
|
||||
}
|
||||
|
||||
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead & binary, AuString &out, ECodePage page)
|
||||
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead &binary, AuString &out, ECodePage page)
|
||||
{
|
||||
auto aaa = DecodeUTF8(binary, {}, page);
|
||||
out.resize(aaa.second);
|
||||
@ -70,4 +72,74 @@ namespace Aurora::Locale::Encoding
|
||||
out.resize(ret.second);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// NEW API
|
||||
|
||||
/// Supporting full 6 byte UTF-8, copies or returns the available streams from @param utf8 to @param utf32
|
||||
|
||||
AUKN_SYM AuStreamReadWrittenPair_t ReadUTF8IntoUTF32ByteString(const Memory::MemoryViewRead &utf8, const Memory::MemoryViewWrite &utf32)
|
||||
{
|
||||
const char *begin = utf8.Begin<const char>();
|
||||
const char *end = utf8.End<const char>();
|
||||
AuUInt32 *begin2 = utf32.Begin<AuUInt32>();
|
||||
AuUInt32 *end2 = utf32.End<AuUInt32>();
|
||||
UTF32::ReadUtf8ByteString(begin2, end2, begin, end);
|
||||
return AuStreamReadWrittenPair_t {begin - utf8.Begin<const char>(), (begin2 - utf32.Begin<AuUInt32>()) * sizeof(AuUInt32)};
|
||||
}
|
||||
|
||||
|
||||
/// Supporting full 6 byte UTF-8, copies or returns the available streams from @param utf32 to @param utf8
|
||||
AUKN_SYM AuStreamReadWrittenPair_t ReadUTF32IntoUTF8ByteString(const Memory::MemoryViewRead &utf32, const Memory::MemoryViewWrite &utf8)
|
||||
{
|
||||
const AuUInt32 *begin = utf32.Begin<const AuUInt32>();
|
||||
const AuUInt32 *end = utf32.End<const AuUInt32>();
|
||||
char *dest = utf8.Begin<char>();
|
||||
char *destEnd = utf8.End<char>();
|
||||
|
||||
AuUInt32 counter {};
|
||||
const AuUInt32 *cur = begin;
|
||||
for (; cur < end; cur++)
|
||||
{
|
||||
UTF32::WriteCp(*cur, dest, counter, destEnd - dest);
|
||||
}
|
||||
|
||||
return AuStreamReadWrittenPair_t {(cur - begin) * sizeof(AuUInt32), dest - utf8.Begin<char>()};
|
||||
}
|
||||
|
||||
AUKN_SYM void SwapUTF32(const Memory::MemoryViewWrite &utf32)
|
||||
{
|
||||
UTF32::SwapU32(utf32.Begin<AuUInt32>(), utf32.ToCount<AuUInt32>());
|
||||
}
|
||||
|
||||
AUKN_SYM void SwapUTF16(const Memory::MemoryViewWrite &utf32)
|
||||
{
|
||||
UTF16::SwapU16(utf32.Begin<AuUInt32>(), utf32.ToCount<AuUInt32>());
|
||||
}
|
||||
|
||||
AUKN_SYM AuUInt32 CountUTF32Length(const Memory::MemoryViewRead &utf32, bool bytes)
|
||||
{
|
||||
return UTF32::Count32(utf32.ptr, utf32.length, bytes);
|
||||
}
|
||||
|
||||
AUKN_SYM AuUInt32 CountUTF16Length(const Memory::MemoryViewRead &utf16, bool bytes)
|
||||
{
|
||||
return UTF16::Count16(utf16.ptr, utf16.length, bytes);
|
||||
}
|
||||
|
||||
AUKN_SYM AuUInt32 CountUTF8Length(const Memory::MemoryViewRead &utf8, bool bytes)
|
||||
{
|
||||
auto pair = ReadUTF8IntoUTF32ByteString(utf8, {});
|
||||
return bytes ? pair.first : pair.second / sizeof(AuUInt32);
|
||||
}
|
||||
|
||||
AUKN_SYM AuUInt32 CountSJISLength(const Memory::MemoryViewRead &sjis, bool bytes)
|
||||
{
|
||||
return SJIS::CountSJIS(sjis.ptr, sjis.length, bytes);
|
||||
}
|
||||
|
||||
AUKN_SYM AuUInt32 CountGBK16Length(const Memory::MemoryViewRead &gbk, bool bytes)
|
||||
{
|
||||
return GBK::CountGbk(gbk.ptr, gbk.length, bytes);
|
||||
}
|
||||
}
|
||||
|
@ -7,117 +7,29 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "GBK/GBK.hpp"
|
||||
#include "SJIS/SJIS.hpp"
|
||||
#include "UTFn/AuUTF8.hpp"
|
||||
#include "UTFn/AuUTF16.hpp"
|
||||
#include "UTFn/AuUTF32.hpp"
|
||||
|
||||
namespace Aurora::Locale::Encoding
|
||||
{
|
||||
AuStreamReadWrittenPair_t DecodeUTF8(void *binary, AuUInt32 binaryLength, AuString &out, ECodePage page = ECodePage::eUnsupported);
|
||||
|
||||
template <bool optimized = false>
|
||||
struct TextStreamDecoderImpl
|
||||
struct TextStreamProcessor
|
||||
{
|
||||
bool readHeader {};
|
||||
ECodePage page = ECodePage::eUnsupported;
|
||||
ECodePage defaultPage = ECodePage::eUnsupported;
|
||||
EncoderAdapter state;
|
||||
|
||||
TextStreamDecoderImpl(ECodePage page = ECodePage::eSysUnk) : defaultPage(page) {}
|
||||
TextStreamProcessor(ECodePage page = ECodePage::eSysUnk) : defaultPage(page) {}
|
||||
|
||||
using TypeIn_t = std::conditional_t<optimized, void *, const void *>;
|
||||
using TypeCast_t = std::conditional_t<optimized, AuUInt8 *, const AuUInt8 *>;
|
||||
using TypeIn_t = const void *;
|
||||
using TypeCast_t = const AuUInt8 *;
|
||||
|
||||
|
||||
static int GetLenSJISCodePoint(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
if (len == 0) return 0;
|
||||
auto b = in[0];
|
||||
if (b >= 0x80)
|
||||
{
|
||||
if (b <= 0xDF)
|
||||
{
|
||||
if (len < 2) return 0;
|
||||
else return 2;
|
||||
}
|
||||
else if (b <= 0xEF)
|
||||
{
|
||||
if (len < 3) return 0;
|
||||
else return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len < 4) return 0;
|
||||
else return 4;
|
||||
}
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetLenSJISString(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
AuUInt32 i;
|
||||
for (i = 0; i < len; )
|
||||
{
|
||||
auto next = GetLenSJISCodePoint(in + i, len - i);
|
||||
if (next == 0) return i;
|
||||
i += next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int GetLenGBKCodePoint(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
if (len == 0) return 0;
|
||||
auto b = in[0];
|
||||
if (b >= 0x80)
|
||||
{
|
||||
if (len < 2) return 0;
|
||||
else return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetLenGBKString(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
AuUInt32 i;
|
||||
for (i = 0; i < len; )
|
||||
{
|
||||
auto next = GetLenGBKCodePoint(in + i, len - i);
|
||||
if (next == 0) return i;
|
||||
i += next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
#define AU_HIGH_SURROGATE_START 0xd800
|
||||
#define AU_HIGH_SURROGATE_END 0xdbff
|
||||
#define AU_LOW_SURROGATE_START 0xdc00
|
||||
#define AU_LOW_SURROGATE_END 0xdfff
|
||||
#define AU_IS_HIGH_SURROGATE(wch) (((wch) >= AU_HIGH_SURROGATE_START) && ((wch) <= AU_HIGH_SURROGATE_END))
|
||||
#define AU_IS_LOW_SURROGATE(wch) (((wch) >= AU_LOW_SURROGATE_START) && ((wch) <= AU_LOW_SURROGATE_END))
|
||||
|
||||
static int GetLenUC2CodePoint(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
if (len < 2) return 0;
|
||||
auto a = *reinterpret_cast<const AuUInt16 *>(in);
|
||||
if (!AU_IS_HIGH_SURROGATE(a)) return 2;
|
||||
|
||||
if (len < 4) return 0;
|
||||
auto b = *reinterpret_cast<const AuUInt16 *>(in + 2);
|
||||
return AU_IS_LOW_SURROGATE(b) ? 4 : 0;
|
||||
}
|
||||
|
||||
static int GetLenUC2String(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
AuUInt32 i;
|
||||
for (i = 0; i < len; )
|
||||
{
|
||||
auto next = GetLenUC2CodePoint(in + i, len - i);
|
||||
if (next == 0) return i;
|
||||
i += next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
AuStreamReadWrittenPair_t EncodeUTF8(TypeIn_t binary, AuUInt32 binaryLength, void *utf8, AuUInt32 utfLen)
|
||||
AuStreamReadWrittenPair_t EncodeUTF8(const void *binary, AuUInt32 binaryLength, void *utf8, AuUInt32 utfLen)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
@ -128,11 +40,11 @@ namespace Aurora::Locale::Encoding
|
||||
{
|
||||
if (page == ECodePage::eUnsupported)
|
||||
{
|
||||
auto header = DecodeBOM(Aurora::Memory::MemoryViewRead(binary, binaryLength));
|
||||
if (header)
|
||||
auto header = DecodeBOM(Memory::MemoryViewRead(binary, binaryLength));
|
||||
if (header.length)
|
||||
{
|
||||
page = header->first;
|
||||
offset = header->second;
|
||||
page = header.page;
|
||||
offset = header.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -155,29 +67,11 @@ namespace Aurora::Locale::Encoding
|
||||
}
|
||||
|
||||
binaryLength = binaryLength - offset;
|
||||
|
||||
if (page == ECodePage::eGBK)
|
||||
{
|
||||
binaryLength = GetLenGBKString(reinterpret_cast<TypeCast_t>(binary) + offset, binaryLength);
|
||||
}
|
||||
else if (page == ECodePage::eSJIS)
|
||||
{
|
||||
binaryLength = GetLenSJISString(reinterpret_cast<TypeCast_t>(binary) + offset, binaryLength);
|
||||
}
|
||||
else if ((page == ECodePage::eUTF16) || (page == ECodePage::eUTF16BE))
|
||||
{
|
||||
binaryLength = GetLenUC2String(reinterpret_cast<TypeCast_t>(binary) + offset, binaryLength);
|
||||
}
|
||||
else if ((page == ECodePage::eUTF32) || (page == ECodePage::eUTF32BE))
|
||||
{
|
||||
binaryLength &= ~3;
|
||||
}
|
||||
|
||||
auto real = state.CPToUTF8(reinterpret_cast<TypeCast_t>(binary) + offset, binaryLength, utf8, utfLen);
|
||||
return AuMakePair(real.first + offset, real.second);
|
||||
}
|
||||
|
||||
AuStreamReadWrittenPair_t EncodeUTF8(TypeIn_t binary, AuUInt32 binaryLength, AuString &out)
|
||||
AuStreamReadWrittenPair_t EncodeUTF8(const void *binary, AuUInt32 binaryLength, AuString &out)
|
||||
{
|
||||
auto preemptive = EncodeUTF8(binary, binaryLength, nullptr, 0);
|
||||
if (!AuTryResize(out, preemptive.second)) return {};
|
||||
@ -237,6 +131,4 @@ namespace Aurora::Locale::Encoding
|
||||
/// 'TextStreamProcessor', a stateful wrapper around DecodeUTF8
|
||||
/// Using this you can handle a stateful, optionally bom prefixed, stream
|
||||
/// Initialization (ie: setting a default codepage) is optional
|
||||
using TextStreamProcessor = typename TextStreamDecoderImpl<false>;
|
||||
using TextStreamProcessorInternal = typename TextStreamDecoderImpl<true>;
|
||||
}
|
36
Source/Locale/Encoding/GBK/GBK.hpp
Normal file
36
Source/Locale/Encoding/GBK/GBK.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: GBK.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Locale::Encoding::GBK
|
||||
{
|
||||
static inline int GetLenGBKCodePoint(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
if (len == 0) return 0;
|
||||
auto b = in[0];
|
||||
if (b >= 0x80)
|
||||
{
|
||||
if (len < 2) return 0;
|
||||
else return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int CountGbk(const void *base, AuUInt32 length, bool bytes = false)
|
||||
{
|
||||
AuUInt32 i {}, cps {};
|
||||
for (i = 0; i < length; )
|
||||
{
|
||||
auto next = GetLenGBKCodePoint((const AuUInt8*)base + i, length - i);
|
||||
if (next == 0) return i;
|
||||
i += next;
|
||||
cps++;
|
||||
}
|
||||
return bytes ? i : cps;
|
||||
}
|
||||
}
|
67
Source/Locale/Encoding/SJIS/SJIS.hpp
Normal file
67
Source/Locale/Encoding/SJIS/SJIS.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: SJIS.hpp
|
||||
Date: 2021-11-1
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Locale::Encoding::SJIS
|
||||
{
|
||||
static inline int GetLenSJISCodePoint(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
if (len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto b = in[0];
|
||||
|
||||
if (b >= 0x80)
|
||||
{
|
||||
if (b <= 0xDF)
|
||||
{
|
||||
if (len < 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
else if (b <= 0xEF)
|
||||
{
|
||||
if (len < 3)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len < 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline AuUInt32 CountSJIS(const void *base, AuUInt32 length, bool bytes = false)
|
||||
{
|
||||
AuUInt32 i {}, cps {};
|
||||
for (; i < length; )
|
||||
{
|
||||
auto next = GetLenSJISCodePoint((const AuUInt8 *)base + i, length - i);
|
||||
if (next == 0) return i;
|
||||
cps++;
|
||||
i += next;
|
||||
}
|
||||
return bytes ? i : cps;
|
||||
}
|
||||
}
|
53
Source/Locale/Encoding/UTFn/AuUTF16.hpp
Normal file
53
Source/Locale/Encoding/UTFn/AuUTF16.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuUTF16.hpp
|
||||
Date: 2021-10-31
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Locale::Encoding::UTF16
|
||||
{
|
||||
static void SwapU16(void *base, AuUInt32 count)
|
||||
{
|
||||
count *= 2;
|
||||
for (int i = 0; i < count; i += 2)
|
||||
{
|
||||
AuWriteU16BE(base, i, AuReadU16LE(base, i));
|
||||
}
|
||||
}
|
||||
|
||||
#define AU_HIGH_SURROGATE_START 0xd800
|
||||
#define AU_HIGH_SURROGATE_END 0xdbff
|
||||
#define AU_LOW_SURROGATE_START 0xdc00
|
||||
#define AU_LOW_SURROGATE_END 0xdfff
|
||||
#define AU_IS_HIGH_SURROGATE(wch) (((wch) >= AU_HIGH_SURROGATE_START) && ((wch) <= AU_HIGH_SURROGATE_END))
|
||||
#define AU_IS_LOW_SURROGATE(wch) (((wch) >= AU_LOW_SURROGATE_START) && ((wch) <= AU_LOW_SURROGATE_END))
|
||||
|
||||
static int GetLenUC2CodePoint(const AuUInt8 *in, AuUInt32 len)
|
||||
{
|
||||
if (len < 2) return 0;
|
||||
auto a = *reinterpret_cast<const AuUInt16 *>(in);
|
||||
if (!AU_IS_HIGH_SURROGATE(a)) return 2;
|
||||
|
||||
if (len < 4) return 0;
|
||||
auto b = *reinterpret_cast<const AuUInt16 *>(in + 2);
|
||||
return AU_IS_LOW_SURROGATE(b) ? 4 : 0;
|
||||
}
|
||||
|
||||
static int Count16(const void *base, AuUInt32 length, bool bytes = false)
|
||||
{
|
||||
AuUInt32 i {}, cps {};
|
||||
|
||||
for (; i < length; )
|
||||
{
|
||||
auto next = GetLenUC2CodePoint(((const AuUInt8 *)base) + i, length - i);
|
||||
if (next == 0) return i;
|
||||
i += next;
|
||||
cps++;
|
||||
}
|
||||
|
||||
return bytes ? i : cps;
|
||||
}
|
||||
}
|
188
Source/Locale/Encoding/UTFn/AuUTF32.hpp
Normal file
188
Source/Locale/Encoding/UTFn/AuUTF32.hpp
Normal file
@ -0,0 +1,188 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuUTF32.hpp
|
||||
Date: 2021-10-31
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Locale::Encoding::UTF32
|
||||
{
|
||||
static void SwapU32(void *base, AuUInt32 count)
|
||||
{
|
||||
count *= 4;
|
||||
for (int i = 0; i < count; i += 4)
|
||||
{
|
||||
AuWriteU32BE(base, i, AuReadU32LE(base, i));
|
||||
}
|
||||
}
|
||||
|
||||
static AuUInt32 Count32(const void *base, AuUInt32 length, bool bytes = false)
|
||||
{
|
||||
return bytes ? length & ~3 : (length & ~3) / 4;
|
||||
}
|
||||
|
||||
static bool WriteCp(AuUInt32 cp, char *& result, AuUInt32 &counter, AuUInt32 max)
|
||||
{
|
||||
if (cp < 0x80)
|
||||
{
|
||||
if (counter + 1 > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
counter += 1;
|
||||
*(result++) = static_cast<AuUInt8>(cp);
|
||||
}
|
||||
else if (cp < 0x800)
|
||||
{
|
||||
if (counter + 2 > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
counter += 2;
|
||||
*(result++) = static_cast<AuUInt8>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<AuUInt8>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000)
|
||||
{
|
||||
if (counter + 3 > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
counter += 3;
|
||||
*(result++) = static_cast<AuUInt8>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 200000)
|
||||
{
|
||||
if (counter + 4 > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
counter += 4;
|
||||
*(result++) = static_cast<AuUInt8>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 400000)
|
||||
{
|
||||
if (counter + 5 > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
counter += 5;
|
||||
*(result++) = static_cast<AuUInt8>((cp >> 24) | 0xf8);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 18) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 80000000)
|
||||
{
|
||||
if (counter + 6 > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
counter += 6;
|
||||
*(result++) = static_cast<AuUInt8>((cp >> 30) | 0xfc);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 24) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 18) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<AuUInt8>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ReadUtf8ByteString(AuUInt32 *cp, AuUInt32 *&cpEnd, const char *&s, const char *end)
|
||||
{
|
||||
if (!s)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cp)
|
||||
{
|
||||
cpEnd = cp;
|
||||
}
|
||||
|
||||
auto &it = s;
|
||||
|
||||
while ((it < end) && // can coomsome
|
||||
(cp && cp < cpEnd))
|
||||
{
|
||||
AuUInt32 c = 0;
|
||||
if (*it <= 0x7FU)
|
||||
{
|
||||
c = *it;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((*it & 0xC0U) != 0xC0U)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AuUInt32 nby = 0;
|
||||
for (AuUInt8 b = *it; (b & 0x80U) != 0; b <<= 1, ++nby)
|
||||
{}
|
||||
|
||||
if (nby > 6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((end - it) < nby)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c = *it & (AuUInt8(0xFFU) >> (nby + 1));
|
||||
|
||||
for (AuUInt32 i = 1; i < nby; ++i)
|
||||
{
|
||||
if ((it[i] & 0xC0U) != 0x80U)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c = (c << 6) | (it[i] & 0x3FU);
|
||||
}
|
||||
|
||||
it += nby;
|
||||
}
|
||||
|
||||
if (cp)
|
||||
{
|
||||
*cp = c;
|
||||
cp++;
|
||||
}
|
||||
|
||||
cpEnd++;
|
||||
}
|
||||
}
|
||||
|
||||
static AuUInt32 CountU8Overhead(AuUInt32 cp)
|
||||
{
|
||||
if (cp < 0x80)
|
||||
return 1;
|
||||
else if (cp < 0x800)
|
||||
return 2;
|
||||
else if (cp < 0x10000)
|
||||
return 3;
|
||||
else if (cp < 200000)
|
||||
return 4;
|
||||
else if (cp < 400000)
|
||||
return 5;
|
||||
else if (cp < 80000000)
|
||||
return 6;
|
||||
}
|
||||
}
|
13
Source/Locale/Encoding/UTFn/AuUTF8.hpp
Normal file
13
Source/Locale/Encoding/UTFn/AuUTF8.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
/***
|
||||
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuUTF8.hpp
|
||||
Date: 2021-10-31
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Locale::Encoding::UTF8
|
||||
{
|
||||
|
||||
}
|
@ -345,7 +345,7 @@ namespace Aurora::Locale
|
||||
gCountryCode = AuToUpper(gCountryCode);
|
||||
gCodeset = AuToUpper(gCodeset);
|
||||
|
||||
LogDbg("Initialized localization information (language: {}, country: {}, codeset: {})", gLanguageCode, gCountryCode, gCodeset);
|
||||
LogDbg("Initialized default localization information (language: {}, country: {}, codeset: {})", gLanguageCode, gCountryCode, gCodeset);
|
||||
}
|
||||
|
||||
static bool gLockLocale = false;
|
||||
|
@ -51,7 +51,7 @@ namespace Aurora::Memory
|
||||
class InternalHeap : public Heap
|
||||
{
|
||||
public:
|
||||
InternalHeap() : base_(nullptr), mutex_(nullptr), heap_(nullptr), _count(0)
|
||||
InternalHeap() : base_(nullptr), mutex_(nullptr), heap_(nullptr), count_(0)
|
||||
{ }
|
||||
|
||||
~InternalHeap();
|
||||
@ -90,14 +90,14 @@ namespace Aurora::Memory
|
||||
AuThreadPrimitives::MutexUnique_t mutex_;
|
||||
void *base_ {};
|
||||
O1HeapInstance *heap_ {};
|
||||
int _count {};
|
||||
AuUInt length_;
|
||||
bool _isDangling {};
|
||||
int count_ {};
|
||||
AuUInt length_ {};
|
||||
bool isDangling_ {};
|
||||
};
|
||||
|
||||
InternalHeap::~InternalHeap()
|
||||
{
|
||||
SysAssertDbgExp(_count == 0);
|
||||
SysAssertDbgExp(count_ == 0);
|
||||
|
||||
if (base_)
|
||||
{
|
||||
@ -149,7 +149,7 @@ namespace Aurora::Memory
|
||||
{
|
||||
if (!heap_) return nullptr;
|
||||
auto ret = o1heapAllocate(heap_, length);
|
||||
if (ret) _count++;
|
||||
if (ret) count_++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -210,14 +210,14 @@ namespace Aurora::Memory
|
||||
{
|
||||
if (buffer == nullptr) return;
|
||||
o1heapFree(heap_, buffer);
|
||||
_count--;
|
||||
count_--;
|
||||
}
|
||||
|
||||
void InternalHeap::TryRelease()
|
||||
{
|
||||
if (!_isDangling) return;
|
||||
if (!isDangling_) return;
|
||||
|
||||
if (_count == 0)
|
||||
if (count_ == 0)
|
||||
{
|
||||
delete this;
|
||||
return;
|
||||
@ -226,13 +226,13 @@ namespace Aurora::Memory
|
||||
|
||||
void InternalHeap::RequestTermination()
|
||||
{
|
||||
if (_count)
|
||||
if (count_)
|
||||
{
|
||||
LogWarn("Heap life was less than its allocations, waiting for final free");
|
||||
LogWarn("Reporting using mayday!");
|
||||
Telemetry::Mayday();
|
||||
|
||||
_isDangling = true;
|
||||
isDangling_ = true;
|
||||
TryRelease();
|
||||
}
|
||||
else
|
||||
|
@ -21,9 +21,9 @@ namespace Aurora::Process
|
||||
exit(exitcode);
|
||||
#else
|
||||
// ???
|
||||
*(AuUInt8 *)0 = 0;
|
||||
*(AuUInt8 *)0xFFFF = 0;
|
||||
*(AuUInt8 *)0xFFFFFFFF = 0;
|
||||
*(AuUInt32 *)0 = exitcode;
|
||||
*(AuUInt32 *)0xFFFF = exitcode;
|
||||
*(AuUInt32 *)0xFFFFFFFF = exitcode;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
#include "Open.Linux.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <Source/IO/FS/FS.hpp>
|
||||
|
||||
namespace Aurora::Processes
|
||||
{
|
||||
@ -30,6 +31,6 @@ namespace Aurora::Processes
|
||||
|
||||
AUKN_SYM void OpenFile(const AuString &file)
|
||||
{
|
||||
LinuxOpenAsync(file);
|
||||
LinuxOpenAsync(IO::FS::NormalizePathRet(file));
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ namespace Aurora::Processes
|
||||
static AuList<AuString> gOpenItems;
|
||||
static AuThreadPrimitives::ConditionMutexUnique_t gCondMutex;
|
||||
static AuThreadPrimitives::ConditionVariableUnique_t gCondVariable;
|
||||
static AuThreading::Threads::ThreadUnique_t gOpenerThread;
|
||||
static AuThreads::ThreadUnique_t gOpenerThread;
|
||||
|
||||
static void RunTasks()
|
||||
{
|
||||
@ -49,9 +49,9 @@ namespace Aurora::Processes
|
||||
{
|
||||
gCondMutex = AuThreadPrimitives::ConditionMutexUnique();
|
||||
gCondVariable = AuThreadPrimitives::ConditionVariableUnique(AuUnsafeRaiiToShared(gCondMutex));
|
||||
gOpenerThread = AuThreading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo(
|
||||
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(OpenerThread)),
|
||||
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}),
|
||||
gOpenerThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
|
||||
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(OpenerThread)),
|
||||
AuThreads::IThreadVectorsFunctional::OnExit_t{}),
|
||||
"COM ShellExecute Runner"
|
||||
));
|
||||
gOpenerThread->Run();
|
||||
|
@ -33,6 +33,9 @@ namespace Aurora::Processes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: where the hell is my release
|
||||
|
||||
class ProcessImpl : public IProcess
|
||||
{
|
||||
public:
|
||||
@ -53,6 +56,7 @@ namespace Aurora::Processes
|
||||
bool Read(bool error, void *buffer, AuUInt32 &len) override;
|
||||
bool Write(const void *buffer, AuUInt32 len) override;
|
||||
|
||||
// TODO: what in the hell this is ugly
|
||||
bool Start(enum ESpawnType type, bool fwdOut, bool fwdErr, bool fwdIn) override;
|
||||
|
||||
private:
|
||||
@ -71,9 +75,9 @@ namespace Aurora::Processes
|
||||
AuList<const char *> cargs_;
|
||||
AuString windowsCli_;
|
||||
|
||||
Threading::Threads::ThreadUnique_t thread_;
|
||||
HANDLE process_ {INVALID_HANDLE_VALUE};;
|
||||
HANDLE hthread_ {INVALID_HANDLE_VALUE};;
|
||||
AuThreads::ThreadUnique_t thread_;
|
||||
HANDLE process_ {INVALID_HANDLE_VALUE};
|
||||
HANDLE hthread_ {INVALID_HANDLE_VALUE};
|
||||
AuSInt exitCode_;
|
||||
};
|
||||
|
||||
@ -327,9 +331,9 @@ namespace Aurora::Processes
|
||||
this->exitCode_ = exitCode;
|
||||
};
|
||||
|
||||
this->thread_ = AuThreading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo(
|
||||
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(a)),
|
||||
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{})
|
||||
this->thread_ = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
|
||||
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(a)),
|
||||
AuThreads::IThreadVectorsFunctional::OnExit_t{})
|
||||
));
|
||||
|
||||
if (!this->thread_)
|
||||
|
@ -58,9 +58,16 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
OSThread::~OSThread()
|
||||
{
|
||||
bool bDetached {};
|
||||
bool bDetachedSuccess {};
|
||||
|
||||
if (contextUsed_)
|
||||
{
|
||||
if (!detached_)
|
||||
if (detached_)
|
||||
{
|
||||
bDetached = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Exit();
|
||||
WaitFor(terminated_.get());
|
||||
@ -69,7 +76,25 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
terminated_.reset();
|
||||
FreeOSContext();
|
||||
HookReleaseThreadResources();
|
||||
|
||||
if (bDetached)
|
||||
{
|
||||
if (this->tlsReferenceThread_)
|
||||
{
|
||||
AU_LOCK_GUARD(this->tlsLock_);
|
||||
AU_LOCK_GUARD(this->tlsReferenceThread_->tlsLock_);
|
||||
|
||||
std::exchange(this->tlsReferenceThread_->tls_, this->tls_);
|
||||
std::exchange(this->tlsReferenceThread_->threadFeatures_, this->threadFeatures_);
|
||||
|
||||
bDetachedSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDetachedSuccess || !bDetached)
|
||||
{
|
||||
HookReleaseThreadResources();
|
||||
}
|
||||
}
|
||||
|
||||
// Called whenever an aurora thread is exiting
|
||||
@ -92,9 +117,13 @@ namespace Aurora::Threading::Threads
|
||||
}
|
||||
}
|
||||
|
||||
void OSThread::AddLastHopeTlsHook(const AuSPtr<Threading::Threads::IThreadFeature> &feature)
|
||||
void OSThread::AddLastHopeTlsHook(const AuSPtr<AuThreads::IThreadFeature> &feature)
|
||||
{
|
||||
// TODO: mutex
|
||||
if (!feature)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AuTryInsert(threadFeatures_, feature);
|
||||
}
|
||||
|
||||
@ -248,13 +277,14 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
void OSThread::SetAffinity(AuUInt32 mask)
|
||||
{
|
||||
if (mask == 0) mask = 0xFFFFFFFF;
|
||||
affinityProcessMask_ = mask;
|
||||
UpdateAffinity(mask);
|
||||
}
|
||||
|
||||
void OSThread::SetPrio(EThreadPrio prio)
|
||||
{
|
||||
prio_ = prio;
|
||||
if (!EThreadPrioIsValid(prio)) return;
|
||||
UpdatePrio(prio);
|
||||
}
|
||||
|
||||
@ -292,7 +322,7 @@ namespace Aurora::Threading::Threads
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto ok = _beginthreadex(nullptr, info_.stackSize.value_or(0), OSEP_f, this, 0, 0);
|
||||
auto ok = _beginthreadex(nullptr, info_.stackSize, OSEP_f, this, 0, 0);
|
||||
if (ok == -1L)
|
||||
{
|
||||
SysPushErrorGen("Couldn't initialize thread: {}", GetName());
|
||||
@ -318,9 +348,9 @@ namespace Aurora::Threading::Threads
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info_.stackSize.has_value())
|
||||
if (info_.stackSize)
|
||||
{
|
||||
ret = pthread_attr_setstacksize(&tattr, std::min(AuUInt32(PTHREAD_STACK_MIN), info_.stackSize.value()));
|
||||
ret = pthread_attr_setstacksize(&tattr, std::min(AuUInt32(PTHREAD_STACK_MIN), info_.stackSize));
|
||||
if (ret != 0)
|
||||
{
|
||||
SysPushErrorGen("Couldn't create thread: {}", GetName());
|
||||
@ -346,6 +376,11 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
void OSThread::_ThreadEP()
|
||||
{
|
||||
// Poke TLS reference thread entity
|
||||
// TODO: we need an internal OSThread *TryPokeTLSThread()
|
||||
auto osThread = static_cast<OSThread *>(GetThread());
|
||||
this->tlsReferenceThread_ = osThread;
|
||||
|
||||
OSAttach();
|
||||
task_();
|
||||
Exit(true);
|
||||
@ -449,7 +484,7 @@ namespace Aurora::Threading::Threads
|
||||
this->unixThreadId_ = 0; // !!!!
|
||||
#endif
|
||||
UpdatePrio(prio_);
|
||||
UpdateAffinity(affinityProcessMask_);
|
||||
SetAffinity(affinityProcessMask_);
|
||||
UpdateName();
|
||||
}
|
||||
|
||||
@ -529,6 +564,19 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
// fall through on error
|
||||
}
|
||||
else if (prio_ == EThreadPrio::ePrioRT)
|
||||
{
|
||||
int policyNonRT =
|
||||
#if defined(AURORA_IS_XNU_DERIVED)
|
||||
SCHED_FIFO;
|
||||
#else
|
||||
SCHED_OTHER;
|
||||
#endif
|
||||
|
||||
sched_param param {};
|
||||
param.sched_priority = sched_get_priority_min(policyNonRT);
|
||||
pthread_setschedparam(handle_, policyNonRT, ¶m);
|
||||
}
|
||||
|
||||
const int *val;
|
||||
if (!AuTryFind(kNiceMap, prio, val))
|
||||
@ -540,7 +588,9 @@ namespace Aurora::Threading::Threads
|
||||
{
|
||||
setpriority(PRIO_PROCESS, tid, *val);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
prio_ = prio;
|
||||
}
|
||||
|
||||
void OSThread::UpdateAffinity(AuUInt32 mask)
|
||||
|
@ -62,6 +62,7 @@ namespace Aurora::Threading::Threads
|
||||
private:
|
||||
Primitives::SpinLock tlsLock_;
|
||||
AuSPtr<TLSView> tls_;
|
||||
OSThread * tlsReferenceThread_ {};
|
||||
AuString name_;
|
||||
ThreadInfo info_;
|
||||
AuUInt32 affinityProcessMask_ = 0xFFFFFFFF;
|
||||
|
Loading…
Reference in New Issue
Block a user