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:
Reece Wilson 2021-11-05 17:34:23 +00:00
parent 4109852d06
commit 99c5e1fa65
90 changed files with 4593 additions and 2258 deletions

192
.gitignore vendored
View File

@ -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_CompilerWorkingDirectory/*
Build_Developers/* Build_Developers/*
Build_Ship/* Build_Ship/*
Build_Internal/* Build_Internal/*
Build_Develop/* Build_Develop/*
*.vcxproj Build_Stage/*
*.vcxproj.filters Build_Ship/*
*.vcxproj.user Build_Workspace/*
# License Headers VS extension
*.licenseheader *.licenseheader
# Binaries / object files
*.dll *.dll
*.exe *.exe
*.obj *.obj
*.so *.so
*.so.*
*.la
*.lai
*.pdb
*.idb
*.exe~
*.obj
*.dynlib *.dynlib
*.dylib
*.lib *.lib
*.d *.d
*.o *.o
*.a
*.la
*.slo
*.lo
*.out *.out
.vs # go unit test
*.test
# Autogenerated project files
compile_flags.txt compile_flags.txt
*.mk *.mk
*.project *.project
*cmake *cmake
Makefile
*.vcxproj
*.xcodeproj
# IDE trash
.vscode
.vs
/*.gcno
.intellij .intellij
.clion .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

View File

@ -7,8 +7,8 @@
"staticImpDefines": "AURORA_ENGINE_KERNEL_STATIC", "staticImpDefines": "AURORA_ENGINE_KERNEL_STATIC",
"defines": [], "defines": [],
"soft-depends": ["wxwidgets", "glm"], "soft-depends": ["wxwidgets", "glm"],
"depends": ["AuroraInterfaces", "mimalloc", "uuid", "fmt", "json", "bzip2", "ltc", "o1heap", "zstd", "zlib", "lz4", "mbedtls"], "depends": ["AuroraInterfaces", "AuroraEnum", "mimalloc", "uuid", "fmt", "json", "bzip2", "ltc", "o1heap", "zstd", "zlib", "lz4", "mbedtls"],
"include-depends": ["fmt", "uuid", "AuroraInterfaces"], "include-depends": ["fmt", "uuid", "AuroraInterfaces", "AuroraEnum"],
"features": ["guess-platform-code"], "features": ["guess-platform-code"],
"linkSources": "Source/Alloc.cpp", "linkSources": "Source/Alloc.cpp",
"actions": [ "actions": [
@ -19,4 +19,4 @@
} }
} }
] ]
} }

View File

@ -7,305 +7,52 @@
***/ ***/
#pragma once #pragma once
namespace Aurora::Loop #include "AsyncTypes.hpp"
{
class ILoopSource; #include "IWorkItem.hpp"
} #include "IWorkItemHandler.hpp"
#include "IThreadPool.hpp"
#include "IAsyncApp.hpp"
#include "Jobs.hpp"
#include "Tasks.hpp"
namespace Aurora::Async namespace Aurora::Async
{ {
class IWorkItem; AUKN_SYM IAsyncApp *GetAsyncApp();
class IAsyncApp;
struct AVoid ///
{ AUKN_SYM WorkerPId_t GetCurrentWorkerPId();
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;
};
/// 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 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(); AUKN_SYM AuSPtr<IWorkItem> NewFence();
class IAsyncApp /// Allocates a new thread pool for usage
{ AUKN_SYM AuSPtr<IThreadPool> NewThreadPool();
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;
// Spawning // TODO: move the following trash out of here
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;
};
#pragma region EASE_OF_READING #pragma region EASE_OF_READING
struct BasicWorkStdFunc : IWorkItemHandler struct BasicWorkStdFunc : IWorkItemHandler
{ {
std::function<void()> callback; AuFunction<void()> callback;
std::function<void()> shutdown; // error 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: private:
@ -315,7 +62,7 @@ namespace Aurora::Async
try try
{ {
callback(); callback();
} }
catch (...) catch (...)
{ {
Debug::PrintError(); Debug::PrintError();
@ -330,7 +77,7 @@ namespace Aurora::Async
{ {
shutdown(); shutdown();
} }
} }
catch (...) catch (...)
{ {
Debug::PrintError(); Debug::PrintError();
@ -341,214 +88,9 @@ namespace Aurora::Async
#if !defined(_CPPSHARP) #if !defined(_CPPSHARP)
/// @hideinitializer /// @hideinitializer
struct BasicWorkCtx : WorkPriv template<typename Frame_t = AuFunction<void()>, typename Cleanup_t = AuFunction<void()>>
{
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()>>
struct WorkItemCallable : IWorkItemHandler struct WorkItemCallable : IWorkItemHandler
{ {
Frame_t frame; Frame_t frame;
@ -581,12 +123,13 @@ namespace Aurora::Async
cleanup(); cleanup();
} }
}; };
#define ASYNC_ERROR(exp) { if constexpr (std::is_same_v<T, bool>) { SysPushErrorGen(exp); return {}; } else { throw std::string(exp); } } #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; } } #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)> 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 {}; if (!func) return {};
return [=](Args&&... in) -> T return [=](Args&&... in) -> T
@ -594,26 +137,28 @@ namespace Aurora::Async
auto work = AuMakeShared<BasicWorkStdFunc>([=]() -> void { auto work = AuMakeShared<BasicWorkStdFunc>([=]() -> void {
func(in...); 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); 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(); workItem->Dispatch();
ASYNC_FINISH; 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)> 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); 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)> 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"); if (!work) ASYNC_ERROR("can't dispatch async call; out of memory");
work.task.onProcess = [=](const AVoid &) -> B 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_ERROR
#undef ASYNC_FINISH #undef ASYNC_FINISH
@ -701,3 +183,11 @@ namespace Aurora::Async
#pragma endregion EASE_OF_READING #pragma endregion EASE_OF_READING
} }
#if !defined(_CPPSHARP)
#include "JobFrom.hpp"
#include "TaskFrom.hpp"
#include "WorkPairImpl.hpp"
#include "WorkBasic.hpp"
#endif

View 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;
};
}

View 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;
};
}

View 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;
};
}

View 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;
};
}

View 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; }
};
}

View 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;
}
}

View 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 &);
};
}

View 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;
}
}

View 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>;
}

View 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);
}
}

View 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
}

View File

@ -12,10 +12,10 @@
namespace Aurora::Async namespace Aurora::Async
{ {
struct WorkerId_t; struct WorkerId_t;
struct WorkerPId_t;
} }
#include "ICommandSubscriber.hpp" #include "ICommandSubscriber.hpp"
#include "ITextLineSubscriber.hpp"
namespace Aurora::Console::Commands namespace Aurora::Console::Commands
{ {
@ -30,20 +30,15 @@ namespace Aurora::Console::Commands
* Parses `string` and dispatches the parsed command on the current thread instantly * Parses `string` and dispatches the parsed command on the current thread instantly
*/ */
AUKN_SYM bool DispatchCommandThisThread(const AuString &string); AUKN_SYM bool DispatchCommandThisThread(const AuString &string);
/** /**
* Parses `string` on the current thread and then schedules the ICommandSubscriber callback on the specified thread * 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); 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); AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerPId_t id);
/**
* 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);
} }

View File

@ -16,16 +16,17 @@ namespace Aurora::Console
AUKN_SYM void WriteLine(const ConsoleMessage &msg); AUKN_SYM void WriteLine(const ConsoleMessage &msg);
/// Consider using the following function for asynchronous utf-8 processed line based input - /// Consider using the following function for asynchronous utf-8 processed line based input -
/// Aurora::Console::Commands::SetCallbackAndDisableCmdProcessing(...) /// Hooks::SetCallbackAndDisableCmdProcessing(...)
/// [!!!] nonblocking
/// [!!!] you must disable the stdio console logger before using this api
AUKN_SYM AuUInt32 ReadStdIn(void *buffer, AuUInt32 length); AUKN_SYM AuUInt32 ReadStdIn(void *buffer, AuUInt32 length);
/// [!!!] nonblocking /// Consider using AuLog for general purpose use
/// [!!!] you should disable the stdio console logger before using this api AUKN_SYM AuUInt32 WriteStdOut(const void *buffer, AuUInt32 length);
/// [!!!] expect system locale if the logger is not enabled
AUKN_SYM AuUInt32 WriteStdIn(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 OpenLateStd();
AUKN_SYM void OpenLateGUI(); AUKN_SYM void OpenLateGUI();
} }

View File

@ -8,6 +8,7 @@
#pragma once #pragma once
#include "IConsoleSubscriber.hpp" #include "IConsoleSubscriber.hpp"
#include "ITextLineSubscriber.hpp"
namespace Aurora::Console::Hooks namespace Aurora::Console::Hooks
{ {
@ -19,4 +20,13 @@ namespace Aurora::Console::Hooks
/// @deprecated wont ever remove /// @deprecated wont ever remove
/// Most c++ styleguides would have you chuck an IConsoleSubscriber in your app somewhere /// Most c++ styleguides would have you chuck an IConsoleSubscriber in your app somewhere
AUKN_SYM void AddFunctionalHook(LineHook_cb hook); 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);
} }

View File

@ -7,7 +7,7 @@
***/ ***/
#pragma once #pragma once
namespace Aurora::Console::Commands namespace Aurora::Console::Hooks
{ {
AUKN_INTERFACE(ITextLineSubscriber, AUKN_INTERFACE(ITextLineSubscriber,
AUI_METHOD(void, OnProcessedLineUTF8, (const AuString &, line)) AUI_METHOD(void, OnProcessedLineUTF8, (const AuString &, line))

View 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, ())
)
}

View 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;
};
}

View 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
};
}

View File

@ -2,11 +2,16 @@
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Logging.hpp File: Logging.hpp
Date: 2021-6-9 Date: 2021-9-21
Author: Reece Author: Reece
***/ ***/
#pragma once #pragma once
#include "ILogger.hpp"
#include "IBasicSink.hpp"
#include "IBasicSinkRB.hpp"
#include "Sinks.hpp"
namespace Aurora::Console::Logging namespace Aurora::Console::Logging
{ {
#if defined(_AUHAS_FMT) #if defined(_AUHAS_FMT)
@ -35,9 +40,14 @@ namespace Aurora::Console::Logging
WriteLinef(EAnsiColor::eYellow, "Verbose", line, std::forward<T>(args)...); WriteLinef(EAnsiColor::eYellow, "Verbose", line, std::forward<T>(args)...);
} }
#else #else
#define LogVerboseNoShip(...) #define LogVerboseNoShip(...) DoNothing()
#endif #endif
static inline void DoNothing()
{
}
template<typename ... T> template<typename ... T>
static inline void LogInfo(const AuString &line, T&& ... args) static inline void LogInfo(const AuString &line, T&& ... args)
{ {

View 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);
}

View 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

View File

@ -54,7 +54,7 @@ namespace Aurora::Data
case EDataType::kTypeStructInt32: return 4; case EDataType::kTypeStructInt32: return 4;
case EDataType::kTypeStructUInt64: return 8; case EDataType::kTypeStructUInt64: return 8;
case EDataType::kTypeStructInt64: return 8; case EDataType::kTypeStructInt64: return 8;
default: return 0; default: return 0;
} }
} }
#endif #endif

View File

@ -2,210 +2,345 @@
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Net.hpp File: Net.hpp
Date: 2021-6-9 Date: 2021-9-21
Author: Reece Author: Reece
***/ ***/
#pragma once #pragma once
namespace Aurora::Async
{
struct IThreadPool;
}
namespace Aurora::IO::Net namespace Aurora::IO::Net
{ {
enum TransportProtocol static const AuUInt16 kMagicPortAny = 65535;
{
kProtocolUDP,
kProtocolTCP
};
enum IPProtocol struct INetworkStream;
{ struct IBasicSocket;
kIPProtocolV4, struct IClientSocket;
kIPProtocolV6 struct IServer;
};
AUE_DEFINE(ETransportProtocol, (
eProtocolInvalid,
eProtocolUDP,
eProtocolTCP
));
AUE_DEFINE(EIPProtocol, (
eIPProtocolInvalid,
eIPProtocolV4,
eIPProtocolV6
));
struct IPAddress struct IPAddress
{ {
IPProtocol ip; EIPProtocol ip;
bool hasIP;
AuString address;
union union
{ {
AuUInt8 v4[4]; AuUInt8 v4[4];
AuUInt16 v6[8]; 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 AUE_DEFINE(ESocketInfo, (
{ eByDns,
TransportProtocol protocol; eByEndpoint
IPAddress address; ));
AuUInt16 port;
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 // 0 - destination is a stateless datagram server
// 1 - destination is a psuedo-stateful server // 1 - destination is a psuedo-stateful server
AuUInt UDPTimeout; AuUInt32 UDPTimeoutInMS;
AUKN_SYM AuString toString();
}; };
class INetworkStream; struct ITLSHandshakeAuthenticate
class IBasicSocket; {
class IClientSocket; virtual AuSPtr<Crypto::X509::DecodedCertificate> GetCertificate() = 0;
};
/** AUE_DEFINE(EHandleErrorClass, (
* DTLS/UDP/TCP/TLS -> TRUE = expects another datagram or read pump ePinError,
* -> FALSE = end of socket life eUserDeny,
*/ eBrokenPacket,
using StreamHasData_cb = std::function<bool(const AuSPtr<IClientSocket> &socket)>; eInvalidCipher,
eBadCert
using InitializeSocket_cb = std::function<bool(const AuSPtr<IClientSocket> &socket)>; ));
using ShutdownSocket_cb = std::function<void(const AuSPtr<IClientSocket> &socket)>;
class IClientHasData struct TLSHandshakeError
{ {
public: EHandleErrorClass error;
// DTLS/UDP/TCP/TLS -> TRUE = expects another datagram or read pump AuSPtr<ITLSHandshakeAuthenticate> session;
// FALSE = end of socket life AuString message;
virtual bool OnSocketHasData(const AuSPtr<IClientSocket> &socket) = 0;
}; };
class FunctionalClientHasData : public IClientHasData AUKN_INTERFACE(IClientSubscriber,
{ //
private: AUI_METHOD(void, OnServerConnectSuccess, (const AuSPtr<IClientSocket> &, socket)),
StreamHasData_cb socket_; AUI_METHOD(void, OnServerConnectFailed, (const AuSPtr<IClientSocket> &, socket)),
public:
FunctionalClientHasData(const StreamHasData_cb &socket) : socket_(socket) // DTLS/UDP/TCP/TLS -> TRUE = expects another datagram or read pump
{} // FALSE = end of socket life
FunctionalClientHasData(StreamHasData_cb &&socket) : socket_(std::move(socket)) AUI_METHOD(bool, OnSockeData, (const AuSPtr<IClientSocket> &, socket)),
{}
};
class IClientShutdown
{
public:
virtual void OnSocketShutdown(const AuSPtr<IClientSocket> &socket) = 0;
};
class FunctionalClientShutdown : public IClientShutdown //
{ AUI_METHOD(void, OnSocketError, (const AuSPtr<IClientSocket> &, socket)),
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;
};
class FunctionalClientDoS: public IClientDoS //
{ AUI_METHOD(void, OnSocketShutdown, (const AuSPtr<IClientSocket> &, socket))
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;
virtual void AddSubscriptionShutdown(const AuSPtr<IClientShutdown> &iface) = 0; AUKN_INTERFACE(IClientSubscriberTls,
virtual void RemoveSubscriptionShutdown(const AuSPtr<IClientShutdown> &iface) = 0; 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; AUKN_INTERFACE(IServerSubscriber,
virtual void RemoveSubscriptionDoS(const AuSPtr<IClientDoS> &iface) = 0; AUI_METHOD(bool, OnClientAccept, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
AUI_METHOD(bool, OnClientDoS, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
virtual void AddSubscriptionTlsBreakdown(const AuSPtr<IClientTLSBreakdown> &iface) = 0; AUI_METHOD(void, OnClientError, (const AuSPtr<IServer> &, server, const AuSPtr<IClientSocket> &, socket)),
virtual void RemoveSubscriptionTlsBreakdown(const AuSPtr<IClientTLSBreakdown> &iface) = 0; 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; using Error_t = std::error_code;
class IBasicSocket struct IBasicSocketPrivateContext
{ {
public: // force vtbl
virtual bool IsActive() = 0; virtual ~IBasicSocketPrivateContext()
virtual Error_t GetLastError() = 0; {}
virtual void Shutdown() = 0; };
virtual bool GetLocalEndpoint(Endpoint &out) = 0;
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: AuUInt32 timeStartMs;
AuUInt32 uptimeMs;
virtual IClientEvents *GetClientEvents() = 0; 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 PumpRead() = 0;
virtual bool PumpWrite() = 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; // If memory.ptr is a nullptr, this method immediately returns with the expected write length in memory.out
virtual bool PeakAsync(AuUInt8 *buffer, AuUInt32 &length) = 0; //
virtual bool ReadSync(AuUInt8 *buffer, AuUInt32 &length, bool all = true) = 0; // 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; // When BufferInputStreamAdhoc is called with false / in the default condition, read sync will be constrained by the async buffer
virtual bool WriteAsync(const AuUInt8 *buffer, AuUInt32 length, std::function<void(bool)>) = 0; // 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 AuUInt8 *buffer, AuUInt32 length) = 0; 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 * Sets the internal application buffer size
* Noting that Linux's default network buffer looks like this: * Noting that Linux's default network buffer looks like this:
* Minimum, Initial, Maximum: 10240 87380 12582912 | 10KB, 86KB, 12MB * Minimum, Initial, Maximum: 10240 87380 12582912 | 10KB, 86KB, 12MB
*/ */
virtual AuUInt GetInternalRingBuffer() = 0; virtual AuUInt GetInternalInputRingBuffer() = 0;
virtual bool SetInternalRingBuffer(AuUInt bytes) = 0; virtual bool SetInternalInputRingBuffer(AuUInt bytes) = 0;
/** /**
* Defines the maximum amount of bytes each recieve frame can ingest * 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; virtual AuUInt32 GetRecvLength() = 0;
/** /**
* Enable ad-hoc input buffer ingestion into our internal buffer * Enable ad-hoc input buffer ingestion into our internal buffer
* Only use when PumpRead will not suffice by design (ie: sync apps) * 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: // Connects to the endpoint defined in the ClientConfig
using ConnectCallback_cb = std::function<void(bool success)>; // Completion will be notified by the following callbacks;
virtual bool Connect() = 0; // IClientSubscriber::OnServerConnectSuccess,
virtual void ConnectAsync(ConnectCallback_cb cb) = 0; // 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 struct TlsConnect
@ -214,77 +349,121 @@ namespace Aurora::IO::Net
AuSPtr<IBasicSocket> socket; AuSPtr<IBasicSocket> socket;
}; };
using TlsConnect_cb = std::function<bool(const TlsConnect &info)>; struct SocketConfig
{
StreamConfig stream;
};
struct ServerInfo struct ServerInfo
{ {
Endpoint listen; ConnectionEndpoint listen;
AuUInt32 maxSessions;
SocketConfig clientDefaults;
AuSPtr<IServerSubscriber> serverSubscriber;
}; };
struct TLSServerInfo : ServerInfo struct TLSServerInfo : ServerInfo
{ {
Aurora::Crypto::RSAPair cert; 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; AuSPtr<IClientSubscriberTls> clientSubscriber;
AuString service;
}; };
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 void GetClients(AuList<AuSPtr<IClientSocket>> &clients) = 0;
virtual bool Listen() = 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 NewServer(const ServerInfo &listen, AuSPtr<IServer> &out) = 0;
virtual bool NewTlsServer(const TLSServerInfo &keys, 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 NewClient(const ClientConfig &info, AuSPtr<ILocalClientSocket> &out) = 0;
virtual bool NewTlsClient(const TLSClientInfo &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);
} }

View File

@ -11,6 +11,7 @@ namespace Aurora::Locale
{ {
enum class ECodePage enum class ECodePage
{ {
eUnsupported,
eUTF32, eUTF32,
eUTF32BE, eUTF32BE,
eUTF16, eUTF16,
@ -23,7 +24,6 @@ namespace Aurora::Locale
eSJIS, eSJIS,
eLatin1, eLatin1,
eSysUnk, eSysUnk,
eUnsupported,
eMax = eUnsupported eMax = eUnsupported
}; };
} }

View File

@ -9,18 +9,32 @@
namespace Aurora::Locale::Encoding namespace Aurora::Locale::Encoding
{ {
AUKN_SYM AuStreamReadWrittenPair_t EncodeUTF8(const Memory::MemoryViewRead &utf8, const Memory::MemoryViewWrite &binary, ECodePage page = ECodePage::eUnsupported); struct BOM
static inline AuStreamReadWrittenPair_t EncodeUTF8(const AuString &utf8, const Memory::MemoryViewWrite &binary, ECodePage page = ECodePage::eUnsupported)
{ {
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 // General purpose arbitrary page to UTF8 (AuStrings are UTF-8 - not 16 or 32; bite me)
/// Returns a pair; bytes consumed, bytes written 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, const Memory::MemoryViewWrite &utf8, ECodePage page = ECodePage::eUnsupported);
AUKN_SYM AuStreamReadWrittenPair_t DecodeUTF8(const Memory::MemoryViewRead &binary, AuString &out, 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
} }

View File

@ -193,7 +193,6 @@ namespace Aurora::Memory
} }
} }
auto oldptr = readPtr;
auto skipped = Read(&out, sizeof(T)); auto skipped = Read(&out, sizeof(T));
if (skipped != sizeof(T)) if (skipped != sizeof(T))
{ {

View File

@ -14,15 +14,15 @@ namespace Aurora::Process
{ {
AUKN_SYM AU_NORETURN void Exit(AuUInt32 exitcode); AUKN_SYM AU_NORETURN void Exit(AuUInt32 exitcode);
enum class EModulePath AUE_DEFINE(EModulePath,
{ (
eModulePathCWD, eModulePathCWD,
eModulePathSystemDir, /// /lib/, windir/system32 eModulePathSystemDir, /// /lib/, windir/system32
eModulePathUserDir, /// /usr/lib/ eModulePathUserDir, /// /usr/lib/
eProcessDirectory, /// eProcessDirectory, ///
eOSSpecified, /// LD_LIBRARY_PATH + /etc/ld.so.conf, AddDllDirectory eOSSpecified, /// LD_LIBRARY_PATH + /etc/ld.so.conf, AddDllDirectory
eSpecified eSpecified
}; ));
static AuList<EModulePath> kUserOverloadableSearchPath = {EModulePath::eSpecified, EModulePath::eModulePathCWD, EModulePath::eProcessDirectory, EModulePath::eModulePathUserDir, EModulePath::eModulePathSystemDir}; 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}; static AuList<EModulePath> kAdminOverloadableSearchPath = {EModulePath::eSpecified, EModulePath::eModulePathSystemDir, EModulePath::eProcessDirectory, EModulePath::eModulePathCWD, EModulePath::eModulePathUserDir};
@ -30,12 +30,13 @@ namespace Aurora::Process
struct ModuleLoadRequest struct ModuleLoadRequest
{ {
AuString mod; AuString mod;
AuOptional<AuString> version; AuString version;
AuOptional<AuString> extension; AuString extension;
AuList<AuString> const *specifiedSearchPaths {}; AuList<AuString> const *specifiedSearchPaths {};
AuList<EModulePath> const *searchPath {}; AuList<EModulePath> const *searchPath {};
bool verifyOne {}; // always true if the executable is signed bool verify {}; // always effectively true if the executable is signed and enableMitigations is true
bool verifyAll {}; bool unixCheckPlusX {};
bool enableMitigations {true};
}; };
AUKN_SYM bool LoadModule(const ModuleLoadRequest &request); AUKN_SYM bool LoadModule(const ModuleLoadRequest &request);

View File

@ -30,6 +30,7 @@ namespace Aurora::Processes
/// @param exitOnSpawn atomically destroys the current process <br> /// @param exitOnSpawn atomically destroys the current process <br>
/// some platforms only support this /// 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 Start(enum ESpawnType, bool fwdOut, bool fwdErr, bool fwdIn) = 0;
virtual bool Terminate() = 0; virtual bool Terminate() = 0;

View File

@ -1,4 +1,4 @@
/*** /***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: IRandomDevice.hpp File: IRandomDevice.hpp

View File

@ -19,6 +19,7 @@
#include <AuroraForEach.hpp> #include <AuroraForEach.hpp>
#include <AuroraInterfaces.hpp> #include <AuroraInterfaces.hpp>
#include <AuroraEnum.hpp>
#include "../AuroraMacros.hpp" #include "../AuroraMacros.hpp"
@ -81,6 +82,7 @@ namespace AuData = Aurora::Data;
namespace AuDebug = Aurora::Debug; namespace AuDebug = Aurora::Debug;
namespace AuThreading = Aurora::Threading; namespace AuThreading = Aurora::Threading;
namespace AuThreadPrimitives = Aurora::Threading::Primitives; namespace AuThreadPrimitives = Aurora::Threading::Primitives;
namespace AuThreads = Aurora::Threading::Threads;
namespace AuHwInfo = Aurora::HWInfo; namespace AuHwInfo = Aurora::HWInfo;
namespace AuIO = Aurora::IO; namespace AuIO = Aurora::IO;
namespace AuIOFS = Aurora::IO::FS; namespace AuIOFS = Aurora::IO::FS;
@ -95,6 +97,9 @@ namespace AuTypes = Aurora::Types;
namespace AuLog = Aurora::Console::Logging; namespace AuLog = Aurora::Console::Logging;
namespace AuMemory = Aurora::Memory; namespace AuMemory = Aurora::Memory;
using AuWorkerId_t = AuAsync::WorkerId_t;
using AuWorkerPId_t = AuAsync::WorkerPId_t;
static inline void AuDebugBreak() static inline void AuDebugBreak()
{ {
AuDebug::DebugBreak(); AuDebug::DebugBreak();
@ -108,6 +113,11 @@ namespace Aurora
bool autoCompressOldLogs {false}; bool autoCompressOldLogs {false};
AuUInt32 maxSizeMB {128 * 1024 * 1024}; // these numbers feel insane, but at least we have a max threshold 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 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 struct TelemetryConfigDoc
@ -137,13 +147,21 @@ namespace Aurora
TelemetryConfigDoc defaultConfig; TelemetryConfigDoc defaultConfig;
}; };
struct SocketConsole
{
bool enableLogging {false};
bool binaryProto {false};
AuString path;
AuIONet::ConnectionEndpoint net;
};
struct ConsoleConfig struct ConsoleConfig
{ {
/// Enables Aurora::Console::xxxStd functions; defer to enableStdXX for default logger behaviour /// Enables Aurora::Console::xxxStd functions; defer to enableStdXX for default logger behaviour
bool enableStdPassthrough {}; bool enableStdPassthrough {false};
/// Disables standard, debug, and GUI consoles /// Enables standard, debug, and GUI consoles
bool disableAllConsoles {}; bool enableConsole {true};
/// Attempt to force a terminal emulator host under graphical subsystems /// Attempt to force a terminal emulator host under graphical subsystems
bool forceConsoleWindow {}; bool forceConsoleWindow {};
@ -151,7 +169,7 @@ namespace Aurora
/// Attempt to force a GUI console under command line targets /// Attempt to force a GUI console under command line targets
bool forceToolKitWindow {}; 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 /// In conjunction with !enableStdPassthrough, enables stdout logging
bool enableStdIn {true}; bool enableStdIn {true};
@ -162,14 +180,31 @@ namespace Aurora
/// Use WxWidgets when possible /// Use WxWidgets when possible
bool enableWxWidgets {true}; bool enableWxWidgets {true};
#if 1
/// FIO config /// FIO config
LocalLogInfo fio; LocalLogInfo fio;
#endif
AuString titleBrand = "Aurora SDK Sample"; AuString titleBrand = "Aurora SDK Sample";
AuString supportPublic {"https://git.reece.sx/AuroraSupport/AuroraRuntime/issues"}; AuString supportPublic {"https://git.reece.sx/AuroraSupport/AuroraRuntime/issues"};
AuString supportInternal {"https://jira.reece.sx"}; AuString supportInternal {"https://jira.reece.sx"};
}; };
struct LoggerConfig
{
///
bool enableStdIn {true};
///
bool enableStdOut {true};
/// FIO config
LocalLogInfo fileConfiguration;
/// Socket config
SocketConsole socketConfiguration;
};
struct CryptoConfig struct CryptoConfig
{ {
@ -192,13 +227,16 @@ namespace Aurora
struct AsyncConfig 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 schedularFrequency {2}; // * 0.5 or 1 MS depending on the platform
AuUInt32 sysPumpFrequency {25}; // x amount of schedularFrequencys AuUInt32 sysPumpFrequency {25}; // x amount of schedularFrequencys
}; };
struct FIOConfig struct FIOConfig
{ {
AuOptional<AuString> defaultBrand = "Aurora"; /// You can bypass branding by assigning an empty string to 'defaultBrand'
AuString defaultBrand = "Aurora";
}; };
struct RuntimeStartInfo struct RuntimeStartInfo

View File

@ -9,13 +9,13 @@
namespace Aurora::Threading::Threads namespace Aurora::Threading::Threads
{ {
enum class EThreadPrio AUE_DEFINE(EThreadPrio,
{ (
eInvalid, eInvalid,
ePrioAboveHigh, ePrioAboveHigh,
ePrioHigh, ePrioHigh,
ePrioNormal, ePrioNormal,
ePrioSub, ePrioSub,
ePrioRT ePrioRT
}; ));
} }

View File

@ -34,6 +34,11 @@ namespace Aurora::Threading::Threads
{ {
return Get(); return Get();
} }
T operator *()
{
return Get();
}
TLSVariable& operator =(const T & val) TLSVariable& operator =(const T & val)
{ {

View File

@ -14,10 +14,10 @@ namespace Aurora::Threading::Threads
ThreadInfo() 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) 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; AuSPtr<IThreadVectors> callbacks;
AuOptional<AuUInt32> stackSize; AuUInt32 stackSize;
AuOptional<AuString> name; AuOptional<AuString> name;
}; };
} }

View File

@ -99,7 +99,11 @@ using AuWPtr = AURORA_RUNTIME_AU_WEAK_PTR<T>;
#define AURORA_RUNTIME_AU_UNIQUE_PTR std::unique_ptr #define AURORA_RUNTIME_AU_UNIQUE_PTR std::unique_ptr
#endif #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>; using AuUPtr = AURORA_RUNTIME_AU_UNIQUE_PTR<T, Deleter_t>;
#if !defined(AURORA_RUNTIME_AU_PAIR) #if !defined(AURORA_RUNTIME_AU_PAIR)

View File

@ -550,6 +550,18 @@ struct is_base_of_template_impl_au
template < template <typename...> class base,typename derived> template < template <typename...> class base,typename derived>
using AuIsBaseOfTemplate = typename is_base_of_template_impl_au<base,derived>::type; 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> template<typename T>
static inline bool AuTestBit(T value, AuUInt8 idx) 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)); value &= ~(T(1) << T(idx));
} }
// TODO: AuPopCnt
// TODO: AuBitScanForward
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_ARM) #if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_ARM)
#define AURORA_PERMIT_ARBITRARY_REF #define AURORA_PERMIT_ARBITRARY_REF
#endif #endif

View File

@ -8,16 +8,19 @@
#include <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
#include "Async.hpp" #include "Async.hpp"
#include "Schedular.hpp" #include "Schedular.hpp"
#include "AsyncApp.hpp"
namespace Aurora::Async namespace Aurora::Async
{ {
void InitAsync() void InitAsync()
{ {
InitSched(); InitSched();
InitApp();
} }
void ShutdownAsync() void ShutdownAsync()
{ {
DeinitSched(); DeinitSched();
ReleaseApp();
} }
} }

View File

@ -7,41 +7,12 @@
***/ ***/
#pragma once #pragma once
#include "GroupState.hpp"
#include "ThreadState.hpp"
#include "AsyncRunnable.hpp"
namespace Aurora::Async 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 InitAsync();
void ShutdownAsync(); void ShutdownAsync();
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,87 +7,46 @@
***/ ***/
#pragma once #pragma once
#include "ThreadPool.hpp"
namespace Aurora::Async namespace Aurora::Async
{ {
struct GroupState; void InitApp();
struct ThreadState; void ReleaseApp();
//class WorkItem;
struct AsyncApp : public IAsyncApp, ThreadPool
void DecRunningTasks();
void IncRunningTasks();
class AsyncApp : public IAsyncApp
{ {
public: bool Spawn(WorkerId_t workerId) override;
AsyncApp(); 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 // Main thread logic
void Start() override; void Start() override;
void Main() override; void Main() override;
void Shutdown() override;
bool Exiting() override;
void SetConsoleCommandDispatcher(WorkerId_t id) override; void SetConsoleCommandDispatcher(WorkerId_t id) override;
// Spawning void CleanUpWorker(WorkerId_t wid) override;
bool Spawn(WorkerId_t workerId) override; void CleanWorkerPoolReservedZeroFree() override;
void SetWorkerIdIsThreadRunner(WorkerId_t, bool runner) 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_; AuOptional<WorkerId_t> commandDispatcher_;
}; };
} }

View 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 = {};
}
};
}

View 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;
}
}

View 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;
}
};
}

View File

@ -8,7 +8,8 @@
#include <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
#include "Async.hpp" #include "Async.hpp"
#include "Schedular.hpp" #include "Schedular.hpp"
#include "AsyncApp.hpp" //#include "AsyncApp.hpp"
#include "ThreadPool.hpp"
namespace Aurora::Async namespace Aurora::Async
{ {
@ -17,9 +18,10 @@ namespace Aurora::Async
AuUInt64 ns; AuUInt64 ns;
WorkerId_t target; WorkerId_t target;
AuSPtr<IAsyncRunnable> runnable; AuSPtr<IAsyncRunnable> runnable;
IThreadPoolInternal *pool;
}; };
static Threading::Threads::ThreadUnique_t gThread; static AuThreads::ThreadUnique_t gThread;
static AuThreadPrimitives::MutexUnique_t gSchedLock; static AuThreadPrimitives::MutexUnique_t gSchedLock;
static AuList<SchedEntry> gEntries; static AuList<SchedEntry> gEntries;
@ -56,7 +58,7 @@ namespace Aurora::Async
AuUInt32 counter {}; AuUInt32 counter {};
AuList<SchedEntry> pending; AuList<SchedEntry> pending;
auto thread = Threading::Threads::GetThread(); auto thread = AuThreads::GetThread();
while (!thread->Exiting()) while (!thread->Exiting())
{ {
@ -70,25 +72,29 @@ namespace Aurora::Async
{ {
try try
{ {
static_cast<AsyncApp *>(GetAsyncApp())->Run(entry.target, entry.runnable); entry.pool->Run(entry.target, entry.runnable);
DecRunningTasks(); entry.pool->DecrementTasksRunning();
} }
catch (...) catch (...)
{ {
LogWarn("Dropped scheduled task! Expect a leaky counter!"); if (entry.pool->ToThreadPool()->InRunnerMode())
LogWarn("Would you rather `Why u no exit?!` or `WHY DID U JUST CRASH REEEE` in production?"); {
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(); Debug::PrintError();
} }
} }
counter++; counter++;
if ((!gRuntimeConfig.async.sysPumpFrequency) || ((gRuntimeConfig.async.sysPumpFrequency) && (counter % gRuntimeConfig.async.sysPumpFrequency) == 0)) if ((!gRuntimeConfig.async.sysPumpFrequency) || ((gRuntimeConfig.async.sysPumpFrequency) && (counter % gRuntimeConfig.async.sysPumpFrequency) == 0))
{ {
try try
{ {
if (!std::exchange(gLockedPump, true)) if (!std::exchange(gLockedPump, true))
{ {
NewWorkItem({0, 0}, AuMakeShared<BasicWorkStdFunc>(PumpSysThread))->Dispatch(); NewWorkItem(AuWorkerId_t{0, 0}, AuMakeShared<BasicWorkStdFunc>(PumpSysThread))->Dispatch();
} }
} }
catch (...) catch (...)
@ -98,7 +104,6 @@ namespace Aurora::Async
} }
} }
} }
} }
void InitSched() void InitSched()
@ -114,32 +119,41 @@ namespace Aurora::Async
void StartSched() void StartSched()
{ {
gThread = Threading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo( AU_LOCK_GUARD(gSchedLock);
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(SchedThread)), if (gThread) return;
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{})
gThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(SchedThread)),
AuThreads::IThreadVectorsFunctional::OnExit_t{})
)); ));
gThread->Run(); gThread->Run();
} }
/// @deprecated
void StopSched() 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); AU_LOCK_GUARD(gSchedLock);
IncRunningTasks(); pool->IncrementTasksRunning();
gEntries.push_back({ns, target, runnable}); gEntries.push_back({ns, target, runnable, pool});
} }
void TerminateSceduledTasks(WorkerId_t target) void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target)
{ {
AU_LOCK_GUARD(gSchedLock); AU_LOCK_GUARD(gSchedLock);
for (auto itr = gEntries.begin(); itr != gEntries.end(); ) 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->runnable->CancelAsync();
itr = gEntries.erase(itr); itr = gEntries.erase(itr);

View File

@ -9,11 +9,13 @@
namespace Aurora::Async namespace Aurora::Async
{ {
struct IThreadPoolInternal;
void InitSched(); void InitSched();
void DeinitSched(); void DeinitSched();
void StartSched(); void StartSched();
void StopSched(); void StopSched();
void Schedule(AuUInt64 ns, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable); void Schedule(AuUInt64 ns, IThreadPoolInternal *pool, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
void TerminateSceduledTasks(WorkerId_t target); void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target);
} }

1061
Source/Async/ThreadPool.cpp Normal file

File diff suppressed because it is too large Load Diff

127
Source/Async/ThreadPool.hpp Normal file
View 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_;
};
}

View 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;
};
}

View File

@ -13,12 +13,12 @@
namespace Aurora::Async 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) if (supportsBlocking)
{ {
finishedEvent_ = AuThreadPrimitives::EventUnique(false, true, true); this->finishedEvent_ = AuThreadPrimitives::EventUnique(false, true, true);
SysAssert(finishedEvent_); SysAssert(this->finishedEvent_);
} }
} }
@ -34,7 +34,7 @@ namespace Aurora::Async
{ {
auto dependency = std::reinterpret_pointer_cast<WorkItem>(workItem); auto dependency = std::reinterpret_pointer_cast<WorkItem>(workItem);
AU_LOCK_GUARD(lock); AU_LOCK_GUARD(this->lock);
AU_LOCK_GUARD(dependency->lock); AU_LOCK_GUARD(dependency->lock);
if (dependency->HasFailed()) if (dependency->HasFailed())
@ -43,7 +43,7 @@ namespace Aurora::Async
} }
dependency->waiters_.push_back(shared_from_this()); dependency->waiters_.push_back(shared_from_this());
waitOn_.push_back(workItem); this->waitOn_.push_back(workItem);
} }
if (status) if (status)
@ -53,14 +53,13 @@ namespace Aurora::Async
return AU_SHARED_FROM_THIS; return AU_SHARED_FROM_THIS;
} }
AuSPtr<IWorkItem> WorkItem::WaitFor(const AuList<AuSPtr<IWorkItem>> &workItems) AuSPtr<IWorkItem> WorkItem::WaitFor(const AuList<AuSPtr<IWorkItem>> &workItems)
{ {
bool status {}; bool status {};
{ {
AU_LOCK_GUARD(lock); AU_LOCK_GUARD(this->lock);
for (auto &workItem : workItems) for (auto &workItem : workItems)
{ {
@ -73,7 +72,7 @@ namespace Aurora::Async
} }
dependency->waiters_.push_back(shared_from_this()); 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) 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; return AU_SHARED_FROM_THIS;
} }
AuSPtr<IWorkItem> WorkItem::SetSchedTime(AuUInt32 ms) 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; return AU_SHARED_FROM_THIS;
} }
AuSPtr<IWorkItem> WorkItem::AddDelayTime(AuUInt32 ms) AuSPtr<IWorkItem> WorkItem::AddDelayTime(AuUInt32 ms)
{ {
delayTimeNs_ += AuUInt64(ms) * AuUInt64(1000000); this->delayTimeNs_ += AuUInt64(ms) * AuUInt64(1000000);
return AU_SHARED_FROM_THIS; return AU_SHARED_FROM_THIS;
} }
AuSPtr<IWorkItem> WorkItem::AddDelayTimeNs(AuUInt64 ns) AuSPtr<IWorkItem> WorkItem::AddDelayTimeNs(AuUInt64 ns)
{ {
delayTimeNs_ += ns; this->delayTimeNs_ += ns;
return AU_SHARED_FROM_THIS; return AU_SHARED_FROM_THIS;
} }
@ -129,7 +140,7 @@ namespace Aurora::Async
if (check) if (check)
{ {
if (dispatchPending_) if (this->dispatchPending_)
{ {
return; return;
} }
@ -152,9 +163,9 @@ namespace Aurora::Async
itr = waitOn_.erase(itr); itr = waitOn_.erase(itr);
} }
dispatchPending_ = true; this->dispatchPending_ = true;
if (Time::CurrentClockNS() < dispatchTimeNs_) if (Time::CurrentClockNS() < this->dispatchTimeNs_)
{ {
Schedule(); Schedule();
return; return;
@ -162,7 +173,7 @@ namespace Aurora::Async
if (auto delay = std::exchange(delayTimeNs_, {})) if (auto delay = std::exchange(delayTimeNs_, {}))
{ {
dispatchTimeNs_ = delay + Time::CurrentClockNS(); this->dispatchTimeNs_ = delay + Time::CurrentClockNS();
Schedule(); Schedule();
return; return;
} }
@ -170,21 +181,32 @@ namespace Aurora::Async
SendOff(); SendOff();
} }
float WorkItem::GetPrio()
{
return prio_;
}
void WorkItem::SetPrio(float val)
{
prio_ = val;
}
void WorkItem::CancelAsync() void WorkItem::CancelAsync()
{ {
AU_LOCK_GUARD(lock); AU_LOCK_GUARD(this->lock);
Fail(); Fail();
} }
void WorkItem::RunAsync() void WorkItem::RunAsync()
{ {
AU_LOCK_GUARD(lock); AU_LOCK_GUARD(this->lock);
IWorkItemHandler::ProcessInfo info(true); IWorkItemHandler::ProcessInfo info(true);
info.pool = this->owner_->ToThreadPool();
if (task_) if (this->task_)
{ {
task_->DispatchFrame(info); this->task_->DispatchFrame(info);
} }
switch (info.type) switch (info.type)
@ -225,13 +247,13 @@ namespace Aurora::Async
} }
} }
finished = true; this->finished = true;
if (finishedEvent_) 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); std::reinterpret_pointer_cast<WorkItem>(waiter)->DispatchEx(true);
} }
@ -246,59 +268,69 @@ namespace Aurora::Async
task_->Shutdown(); task_->Shutdown();
} }
for (auto &waiter : waiters_) for (auto &waiter : this->waiters_)
{ {
std::reinterpret_pointer_cast<WorkItem>(waiter)->Fail(); std::reinterpret_pointer_cast<WorkItem>(waiter)->Fail();
} }
waiters_.clear(); this->waiters_.clear();
waitOn_.clear(); this->waitOn_.clear();
if (finishedEvent_) if (this->finishedEvent_)
{ {
finishedEvent_->Set(); this->finishedEvent_->Set();
} }
} }
bool WorkItem::BlockUntilComplete() bool WorkItem::BlockUntilComplete()
{ {
if (!finishedEvent_) return false; if (!this->finishedEvent_) return false;
return static_cast<AsyncApp *>(GetAsyncApp())->WaitFor(this->worker_, AuUnsafeRaiiToShared(finishedEvent_), 0); return this->owner_->WaitFor(this->worker_, AuUnsafeRaiiToShared(this->finishedEvent_), 0);
} }
bool WorkItem::HasFinished() bool WorkItem::HasFinished()
{ {
return finished; return this->finished;
} }
void WorkItem::Cancel() void WorkItem::Cancel()
{ {
AU_LOCK_GUARD(lock); AU_LOCK_GUARD(this->lock);
Fail(); Fail();
} }
bool WorkItem::HasFailed() bool WorkItem::HasFailed()
{ {
return failed; return this->failed;
} }
void WorkItem::Schedule() 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() 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 // If we aren't actually calling a task interface, we may as well just dispatch objects waiting on us from here
RunAsync(); RunAsync();
} }
else 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) AUKN_SYM AuSPtr<IWorkItem> NewWorkItem(const WorkerId_t &worker, const AuSPtr<IWorkItemHandler> &task, bool supportsBlocking)
{ {
@ -306,12 +338,28 @@ namespace Aurora::Async
{ {
return {}; 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() 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() void *WorkItem::GetPrivateData()

View File

@ -7,12 +7,14 @@
***/ ***/
#pragma once #pragma once
#include "ThreadPool.hpp"
namespace Aurora::Async namespace Aurora::Async
{ {
class WorkItem : public IWorkItem, public IAsyncRunnable, public std::enable_shared_from_this<WorkItem> class WorkItem : public IWorkItem, public IAsyncRunnable, public std::enable_shared_from_this<WorkItem>
{ {
public: 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(); ~WorkItem();
AuSPtr<IWorkItem> WaitFor(const AuSPtr<IWorkItem> &workItem) override; AuSPtr<IWorkItem> WaitFor(const AuSPtr<IWorkItem> &workItem) override;
@ -21,6 +23,8 @@ namespace Aurora::Async
AuSPtr<IWorkItem> SetSchedTimeNs(AuUInt64 ns) override; AuSPtr<IWorkItem> SetSchedTimeNs(AuUInt64 ns) override;
AuSPtr<IWorkItem> AddDelayTime(AuUInt32 ms) override; AuSPtr<IWorkItem> AddDelayTime(AuUInt32 ms) override;
AuSPtr<IWorkItem> AddDelayTimeNs(AuUInt64 ns) 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> Then(const AuSPtr<IWorkItem> &next) override;
AuSPtr<IWorkItem> Dispatch() override; AuSPtr<IWorkItem> Dispatch() override;
@ -38,10 +42,14 @@ namespace Aurora::Async
void *GetPrivateData() override; void *GetPrivateData() override;
AuOptional<void *> ToWorkResultT() override; AuOptional<void *> ToWorkResultT() override;
float GetPrio() override;
void SetPrio(float val) override;
private: private:
void DispatchEx(bool check); void DispatchEx(bool check);
AuSPtr<IWorkItemHandler> task_; AuSPtr<IWorkItemHandler> task_;
WorkerId_t worker_; WorkerId_t worker_;
float prio_ = 0.5f;
AuList<AuSPtr<IWorkItem>> waitOn_; AuList<AuSPtr<IWorkItem>> waitOn_;
AuList<AuSPtr<IWorkItem>> waiters_; AuList<AuSPtr<IWorkItem>> waiters_;
AuThreadPrimitives::SpinLock lock; AuThreadPrimitives::SpinLock lock;
@ -52,10 +60,10 @@ namespace Aurora::Async
bool dispatchPending_ {}; bool dispatchPending_ {};
AuUInt64 dispatchTimeNs_ {}; AuUInt64 dispatchTimeNs_ {};
AuUInt64 delayTimeNs_ {}; AuUInt64 delayTimeNs_ {};
IThreadPoolInternal *owner_ {};
void Fail(); void Fail();
void Schedule(); void Schedule();
void SendOff(); void SendOff();
}; };
} }

View File

@ -13,12 +13,12 @@ namespace Aurora::Console::Commands
struct Command; struct Command;
struct CommandDispatch; struct CommandDispatch;
static AuHashMap<AuString, Command> gCommands; static AuHashMap<AuString, Command> gCommands;
static AuList<Hooks::LineHook_cb> gLineCallbacks; static AuList<Hooks::LineHook_cb> gLineCallbacks;
static AuList<CommandDispatch> gPendingCommands; static AuList<CommandDispatch> gPendingCommands;
static auto gMutex = AuThreadPrimitives::MutexUnique(); static auto gMutex = AuThreadPrimitives::MutexUnique();
static auto gPendingCommandsMutex = AuThreadPrimitives::MutexUnique(); static auto gPendingCommandsMutex = AuThreadPrimitives::MutexUnique();
static AuOptional<Async::WorkerId_t> gCommandDispatcher; static Async::WorkerPId_t gCommandDispatcher;
struct Command struct Command
{ {
@ -45,7 +45,7 @@ namespace Aurora::Console::Commands
eAsync 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; Parse::ParseResult res;
AuSPtr<ICommandSubscriber> callback; AuSPtr<ICommandSubscriber> callback;
@ -105,7 +105,7 @@ namespace Aurora::Console::Commands
} }
else else
{ {
Async::DispatchBasicWorkCallback<CommandDispatch>(workerId, Async::DispatchWork<CommandDispatch>(workerId,
Async::TaskFromConsumerRefT<CommandDispatch>([](const CommandDispatch &dispatch) -> void Async::TaskFromConsumerRefT<CommandDispatch>([](const CommandDispatch &dispatch) -> void
{ {
dispatch.callback->OnCommand(dispatch.arguments); dispatch.callback->OnCommand(dispatch.arguments);
@ -134,28 +134,15 @@ namespace Aurora::Console::Commands
{ {
return Dispatch(string, EDispatchType::eNow, {}); return Dispatch(string, EDispatchType::eNow, {});
} }
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerId_t id) 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 DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerPId_t id)
AUKN_SYM bool DispatchRawLine(const AuString &string)
{ {
if (gExternalLineProcessor) return Dispatch(string, EDispatchType::eAsync, id);
{
gExternalLineProcessor->OnProcessedLineUTF8(string);
return true;
}
return DispatchCommand(string);
}
AUKN_SYM void SetCallbackAndDisableCmdProcessing(const AuSPtr<ITextLineSubscriber> &subscriber)
{
gExternalLineProcessor = subscriber;
} }
void UpdateDispatcher(AuOptional<Async::WorkerId_t> target) 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) static void DispatchCommandsFromThis(const AuList<CommandDispatch> &commands)
@ -192,13 +179,16 @@ namespace Aurora::Console::Commands
auto commands = std::exchange(gPendingCommands, {}); auto commands = std::exchange(gPendingCommands, {});
gPendingCommandsMutex->Unlock(); 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); DispatchCommandsFromThis(commands);
} }
else else
{ {
Async::NewWorkItem(gCommandDispatcher.value(),
NewWorkItem(gCommandDispatcher,
AuMakeShared<Async::BasicWorkStdFunc>([&commands]() AuMakeShared<Async::BasicWorkStdFunc>([&commands]()
{ {
DispatchCommandsFromThis(commands); DispatchCommandsFromThis(commands);

View File

@ -26,11 +26,22 @@ namespace Aurora::Console
return ConsoleStd::ReadStdIn(buffer, length); 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); 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() void Init()
{ {
Hooks::Init(); Hooks::Init();

View File

@ -10,9 +10,9 @@
namespace Aurora::Console::ConsoleFIO namespace Aurora::Console::ConsoleFIO
{ {
static AuList<AuUInt8> gLogBuffer; static AuList<AuUInt8> gLogBuffer;
static AuThreadPrimitives::RWLockUnique_t gLogMutex; static AuThreadPrimitives::RWLockUnique_t gLogMutex;
static IO::FS::OpenWriteUnique_t gFileHandle; static AuIOFS::OpenWriteUnique_t gFileHandle;
static const auto &gLogConfig = gRuntimeConfig.console.fio; static const auto &gLogConfig = gRuntimeConfig.console.fio;
@ -21,7 +21,7 @@ namespace Aurora::Console::ConsoleFIO
AuString path; AuString path;
AuString procName; AuString procName;
if (!IO::FS::GetProfileDomain(path)) if ((gLogConfig.writeLogsToUserDir) || (!IO::FS::GetProfileDomain(path)))
{ {
path = "."; path = ".";
} }

View File

@ -32,16 +32,16 @@ namespace Aurora::Console::ConsoleStd
#if defined(ENABLE_STD_CONSOLE) #if defined(ENABLE_STD_CONSOLE)
#if defined(AURORA_IS_MODERNNT_DERIVED) #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 #define DEFAULT_HANDLE_VAL INVALID_HANDLE_VALUE
using StreamHandle_t = HANDLE;
static StreamHandle_t gWin32Thread = INVALID_HANDLE_VALUE; static StreamHandle_t gWin32Thread = INVALID_HANDLE_VALUE;
#elif defined(IO_POSIX_STREAMS) #elif defined(IO_POSIX_STREAMS)
#define IS_STREAM_HANDLE_VALID(h) (h != 0) #define DEFAULT_HANDLE_VAL 0xFFFFFFFF
#define DEFAULT_HANDLE_VAL 0
using StreamHandle_t = int; using StreamHandle_t = int;
#endif #endif
#define IS_STREAM_HANDLE_VALID(h) (h != DEFAULT_HANDLE_VAL)
static bool AsyncReadAnyOrReadStreamBlock(); static bool AsyncReadAnyOrReadStreamBlock();
static const AuMach kLineBufferMax = 2048; static const AuMach kLineBufferMax = 2048;
@ -229,12 +229,12 @@ namespace Aurora::Console::ConsoleStd
if (GetConsoleWindow() == NULL) if (GetConsoleWindow() == NULL)
{ {
if (!gRuntimeConfig.console.forceConsoleWindow) if (!gRuntimeConfig.console.enableConsole)
{ {
return; return;
} }
if (gRuntimeConfig.console.disableAllConsoles) if (!gRuntimeConfig.console.forceConsoleWindow)
{ {
return; return;
} }
@ -353,7 +353,7 @@ namespace Aurora::Console::ConsoleStd
if (line.size()) if (line.size())
{ {
Console::Commands::DispatchRawLine(line); Console::DispatchRawLine(line);
} }
} }

View File

@ -149,7 +149,7 @@ void ConsoleFrame::OnCmd(wxCommandEvent &event)
textbox->Clear(); textbox->Clear();
Aurora::Console::Commands::DispatchRawLine(line); Aurora::Console::DispatchRawLine(line);
} }
WxSplitterLine *ConsoleFrame::NewSplitter(wxSize splitter, wxColor color) 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) || static const auto kEnableDark = (useWin32DarkHack) ||
@ -637,7 +639,7 @@ namespace Aurora::Console::ConsoleWxWidgets
static bool UseWxConsole() static bool UseWxConsole()
{ {
if (gRuntimeConfig.console.disableAllConsoles) if (!gRuntimeConfig.console.enableConsole)
{ {
return false; 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() static void WxWidgetsThreadMain()
{ {
@ -770,18 +772,17 @@ namespace Aurora::Console::ConsoleWxWidgets
if (std::exchange(gConsoleStarted, true)) return; if (std::exchange(gConsoleStarted, true)) return;
gMutex = AuThreadPrimitives::MutexUnique(); gMutex = AuThreadPrimitives::MutexUnique();
if (!gMutex) return;
Aurora::Console::Hooks::AddSubscription(AuUnsafeRaiiToShared(&gConsoleMessageSubscriber)); Aurora::Console::Hooks::AddSubscription(AuUnsafeRaiiToShared(&gConsoleMessageSubscriber));
gWxWidgetsThread = AuThreading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo( gWxWidgetsThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(WxWidgetsThreadMain)), AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(WxWidgetsThreadMain)),
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}), AuThreads::IThreadVectorsFunctional::OnExit_t{}),
"WxWidgets" "WxWidgets"
)); ));
if (!gWxWidgetsThread) if (!gWxWidgetsThread) return;
{
return;
}
gWxWidgetsThread->Run(); gWxWidgetsThread->Run();
} }

View File

@ -12,9 +12,9 @@
namespace Aurora::Console 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: public:
void Init() override; void Init() override;
@ -38,7 +38,7 @@ namespace Aurora::Console
static void LogThreadEP() static void LogThreadEP()
{ {
auto thread = Threading::Threads::GetThread(); auto thread = AuThreads::GetThread();
SlowStartupTasks(); 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 // 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( gWriterThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(LogThreadEP)), AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(LogThreadEP)),
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}), AuThreads::IThreadVectorsFunctional::OnExit_t{}),
"CasualConsoleAsyncWritter" "CasualConsoleAsyncWritter"
)); ));
if (!gWriterThread) if (!gWriterThread)
@ -73,7 +73,7 @@ namespace Aurora::Console
void InitFlusher() void InitFlusher()
{ {
// Add a 'ShutdownFlushHook' object to the main threads TLS hook // Add a 'ShutdownFlushHook' object to the main threads TLS hook
Threading::Threads::GetThread()->AddLastHopeTlsHook(AuMakeShared<ShutdownFlushHook>()); AuThreads::GetThread()->AddLastHopeTlsHook(AuMakeShared<ShutdownFlushHook>());
InitFlushThread(); InitFlushThread();
} }

View File

@ -32,11 +32,16 @@ namespace Aurora::Console::Hooks
AuTryInsert(gLineFunctionalCallbacks, hook); AuTryInsert(gLineFunctionalCallbacks, hook);
} }
AUKN_SYM void SetCallbackAndDisableCmdProcessing(const AuSPtr<Hooks::ITextLineSubscriber> &subscriber)
{
gExternalLineProcessor = subscriber;
}
void WriteLine(const ConsoleMessage &msg) void WriteLine(const ConsoleMessage &msg)
{ {
AU_LOCK_GUARD(gMutex); 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) for (const auto &callback : gLineFunctionalCallbacks)
{ {

View File

@ -9,6 +9,8 @@
namespace Aurora::Console::Hooks namespace Aurora::Console::Hooks
{ {
inline AuSPtr<Hooks::ITextLineSubscriber> gExternalLineProcessor;
void Deinit(); void Deinit();
void Init(); void Init();
void WriteLine(const ConsoleMessage &msg); void WriteLine(const ConsoleMessage &msg);

View File

@ -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) #if defined(DEBUG) || defined(STAGING)
@ -193,6 +199,7 @@ namespace Aurora::Debug
SymInitialize(GetCurrentProcess(), NULL, TRUE); SymInitialize(GetCurrentProcess(), NULL, TRUE);
#endif #endif
// This is really gross :(
AddVectoredExceptionHandler(1, AddVectoredExceptionHandler(1,
[](_EXCEPTION_POINTERS *ExceptionInfo) -> LONG [](_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)); const auto catchableTypeArray = reinterpret_cast<const CatchableTypeArray*>(reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(throwInfo->pCatchableTypeArray));
AuString suffix; AuString suffix;
bool derivedFromException = {};
for (int i = 0; i < catchableTypeArray->nCatchableTypes; i++) 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 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)); 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) if (strnicmp(descriptor->raw_name(), ".?AVException@", AuArraySize(".?AVException@") - 1) == 0)
{ {
@ -258,7 +264,7 @@ namespace Aurora::Debug
suffix = wptr; 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]); auto possibleStringPointer = reinterpret_cast<const char **>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
if (IsReadable(possibleStringPointer)) if (IsReadable(possibleStringPointer))
@ -272,7 +278,7 @@ namespace Aurora::Debug
} }
else if (strncmp(descriptor->raw_name(), kStringRawName.data(), kStringRawName.size()) == 0) 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)) if (IsReadable(possibleStdStringPointer))
{ {
auto string = *possibleStdStringPointer; auto string = *possibleStdStringPointer;

View File

@ -55,10 +55,82 @@ static void Deinit()
Aurora::Processes::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 namespace Aurora
{ {
static bool gRuntimeHasStarted {}; 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) AUKN_SYM void RuntimeStart(const RuntimeStartInfo &info)
{ {
gRuntimeConfig = info; gRuntimeConfig = info;

View File

@ -41,27 +41,27 @@ namespace Aurora::HWInfo
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) #if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
#if defined(AURORA_COMPILER_MSVC) #if defined(AURORA_COMPILER_MSVC)
static inline CPUIdContext cpuid(AuUInt32 a) static CPUIdContext cpuid(AuUInt32 a)
{ {
CPUIdContext context; CPUIdContext context;
__cpuid(context.regs, a); __cpuid(context.regs, a);
return context; return context;
} }
#elif defined(AURORA_COMPILER_CLANG) || defined(AURORA_COMPILER_GCC) #elif defined(AURORA_COMPILER_CLANG) || defined(AURORA_COMPILER_GCC)
static inline CPUIdContext cpuid(AuUInt32 a) static CPUIdContext cpuid(AuUInt32 a)
{ {
CPUIdContext context; CPUIdContext context;
__get_cpuid(a, &context.eax, &context.ebx, &context.ecx, &context.edx); __get_cpuid(a, &context.eax, &context.ebx, &context.ecx, &context.edx);
return context; return context;
} }
#else #else
static inline CPUIdContext cpuid(AuUInt32 a) static CPUIdContext cpuid(AuUInt32 a)
{ {
return {}; return {};
} }
#endif #endif
#else #else
static inline CPUIdContext cpuid(AuUInt32 a) static CPUIdContext cpuid(AuUInt32 a)
{ {
return {}; return {};
} }

View File

@ -216,7 +216,7 @@ namespace Aurora::IO::FS
return; return;
} }
auto hold = std::exchange(this->pin_, {}); // this has side effects auto hold = std::exchange(this->pin_, {});
if (hold->sub_) if (hold->sub_)
{ {

View File

@ -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() static void SetNamespaceDirectories()
{ {
const char *homedir; const char *homedir;
@ -156,22 +174,12 @@ namespace Aurora::IO::FS
homedir = getpwuid(getuid())->pw_dir; 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(gUserLibPath, gUserLibPath2, "/usr/lib");
SetUnixPaths(gSystemLibPath, gSystemLibPath2, "/lib"); SetUnixPaths(gSystemLibPath, gSystemLibPath2, "/lib");
} }
@ -209,23 +217,23 @@ namespace Aurora::IO::FS
static void ChangeDir() static void ChangeDir()
{ {
#if !defined(AU_NO_AU_HOME_BRANDING) #if !defined(AU_NO_AU_HOME_BRANDING)
if (gRuntimeConfig.fio.defaultBrand.has_value()) if (gRuntimeConfig.fio.defaultBrand.size())
{ {
gApplicationData += "/" + gRuntimeConfig.fio.defaultBrand.value() + "/System"; gApplicationData += "/" + gRuntimeConfig.fio.defaultBrand;
#if defined(AURORA_IS_POSIX_DERIVED) gHomeDirectory += "/" + gRuntimeConfig.fio.defaultBrand;
gHomeDirectory += "/." + gRuntimeConfig.fio.defaultBrand.value() + "/Profile";
#else
gHomeDirectory += "/" + gRuntimeConfig.fio.defaultBrand.value() + "/Profile";
#endif
} }
else
#endif #endif
{
gHomeDirectory += "/.config"; //most unix programs hide their private user data under here
}
NormalizePath(gApplicationData); NormalizePath(gApplicationData);
NormalizePath(gHomeDirectory); 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 // Noting we append a path splitter to prevent hair pulling over missing path delimiters
// Eg: GetHome() + "myAwesomeApp/Config" = %HOME%/Aurora/ProfilemyAwsomeApp/Config // Eg: GetHome() + "myAwesomeApp/Config" = %HOME%/Aurora/ProfilemyAwsomeApp/Config

View File

@ -2,6 +2,8 @@
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Net.cpp File: Net.cpp
Date: 2021-6-17 Date: 2021-7-2
Author: Reece Author: Reece
***/ ***/
#include <Source/RuntimeInternal.hpp>
#include "Net.hpp"

View File

@ -2,6 +2,9 @@
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Net.hpp File: Net.hpp
Date: 2021-6-17 Date: 2021-7-2
Author: Reece Author: Reece
***/ ***/
#pragma once
#include "SocketStatChannel.hpp"

View 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};
};
}

View 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);
}
};
}

View File

@ -255,11 +255,6 @@ namespace Aurora::Locale::Encoding
return ret; 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) AuStreamReadWrittenPair_t STLCPToUTF8(ECodePage page, const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max)
{ {
return DecodeUTF8Internal(in, length, utf8, utf8Max, page); return DecodeUTF8Internal(in, length, utf8, utf8Max, page);

View File

@ -19,5 +19,4 @@ namespace Aurora::Locale::Encoding
AuStreamReadWrittenPair_t STLCPToUTF8(ECodePage page, const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max); 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 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);
} }

View File

@ -31,41 +31,13 @@ namespace Aurora::Locale::Encoding
this->decode = decode; this->decode = decode;
} }
AuStreamReadWrittenPair_t EncoderAdapter::CPToUTF8(void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max) bool EncoderAdapter::TestPage(ECodePage ref)
{ {
AuStreamReadWrittenPair_t ret {}; return (((page == ECodePage::eSysUnk) &&
(GetInternalCodePage() == ref)) ||
if (!length) (page == ref));
{
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;
} }
AuStreamReadWrittenPair_t EncoderAdapter::CPToUTF8(const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max) AuStreamReadWrittenPair_t EncoderAdapter::CPToUTF8(const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Max)
{ {
AuStreamReadWrittenPair_t ret {}; AuStreamReadWrittenPair_t ret {};
@ -80,21 +52,42 @@ namespace Aurora::Locale::Encoding
return {}; return {};
} }
if (((page == ECodePage::eSysUnk) && // decode using internal and/or optimized apis first
(GetInternalCodePage() == ECodePage::eUTF8)) || if (TestPage(ECodePage::eUTF8))
(page == ECodePage::eUTF8))
{ {
length = CountUTF8Length({in, length}, true);
auto readable = std::min(length, utf8Max); auto readable = std::min(length, utf8Max);
if (utf8 && in) if (utf8 && in)
{ {
std::memcpy(utf8, in, readable); 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); ret = Win32CPToUTF8(page, in, length, utf8, utf8Max);
if (!ret.first) if (!ret.first)
{ {
// TODO: iconv support here
ret = STLCPToUTF8(page, in, length, utf8, utf8Max); ret = STLCPToUTF8(page, in, length, utf8, utf8Max);
} }
@ -115,9 +108,7 @@ namespace Aurora::Locale::Encoding
return {}; return {};
} }
if (((page == ECodePage::eSysUnk) && if (TestPage(ECodePage::eUTF8))
(GetInternalCodePage() == ECodePage::eUTF8)) ||
(page == ECodePage::eUTF8))
{ {
auto readable = std::min(utf8Length, cpLen); auto readable = std::min(utf8Length, cpLen);
if (utf8 && cp) if (utf8 && cp)
@ -130,6 +121,7 @@ namespace Aurora::Locale::Encoding
ret = Win32UTF8ToCp(page, utf8, utf8Length, cp, cpLen); ret = Win32UTF8ToCp(page, utf8, utf8Length, cp, cpLen);
if (!ret.first) if (!ret.first)
{ {
// TODO: iconv support here
ret = STLUTF8ToCp(page, utf8, utf8Length, cp, cpLen); ret = STLUTF8ToCp(page, utf8, utf8Length, cp, cpLen);
} }

View File

@ -19,8 +19,8 @@ namespace Aurora::Locale::Encoding
~EncoderAdapter(); ~EncoderAdapter();
void Init(ECodePage page, bool decode); void Init(ECodePage page, bool decode);
bool TestPage(ECodePage page);
AuStreamReadWrittenPair_t CPToUTF8(void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Len);
AuStreamReadWrittenPair_t CPToUTF8(const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Len); AuStreamReadWrittenPair_t CPToUTF8(const void *in, AuUInt32 length, void *utf8, AuUInt32 utf8Len);
AuStreamReadWrittenPair_t UTF8ToCp(const void *utf8, AuUInt32 utf8Length, void *cp, AuUInt32 cpLen); AuStreamReadWrittenPair_t UTF8ToCp(const void *utf8, AuUInt32 utf8Length, void *cp, AuUInt32 cpLen);
}; };

View File

@ -146,48 +146,6 @@ namespace Aurora::Locale::Encoding
#endif #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 Win32CPToUTF8(ECodePage page, const void *in, AuUInt length, void *utf8, AuUInt32 utf8Max)
{ {
AuStreamReadWrittenPair_t ret {}; AuStreamReadWrittenPair_t ret {};

View File

@ -14,7 +14,6 @@ namespace Aurora::Locale::Encoding
AuStreamReadWrittenPair_t Win32ConvertFromUTF8ToCp(AuUInt32 cp, const void *utf8, AuUInt utf8Length, void *cpBlob, AuUInt32 cpLen); 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 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 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); AuStreamReadWrittenPair_t Win32UTF8ToCp(ECodePage page, const void *utf8, AuUInt32 utf8Length, void *cp, AuUInt32 cpLen);
} }

View File

@ -11,10 +11,10 @@
namespace Aurora::Locale::Encoding 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} #define ADD_PATTERN(str, code) {str, {ECodePage::code, AuArraySize(str) - 1}}
AuList<std::tuple<const char *, int, ECodePage>> bows = AuList<std::tuple<const char *, BOM>> bows =
{ {
ADD_PATTERN("\xFF\xFE\x00\x00", eUTF32), ADD_PATTERN("\xFF\xFE\x00\x00", eUTF32),
ADD_PATTERN("\x00\x00\xFE\xFF", eUTF32BE), ADD_PATTERN("\x00\x00\xFE\xFF", eUTF32BE),
@ -30,30 +30,32 @@ namespace Aurora::Locale::Encoding
}; };
#undef ADD_PATTERN #undef ADD_PATTERN
for (const auto &[string, length, category] : bows) for (const auto &[string, bom] : bows)
{ {
if (binary.length < length) continue; if (binary.length < bom.length) continue;
if (std::memcmp(binary.ptr, string, length) != 0) continue; if (std::memcmp(binary.ptr, string, bom.length) != 0) continue;
return AuMakePair(category, length); return bom;
} }
return {}; 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); TextStreamEncoder re(page);
return re.DecodeUTF8(utf8.ptr, utf8.length, binary.ptr, binary.length); 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); TextStreamProcessor re(page);
return re.EncodeUTF8(binary.ptr, binary.length, utf8.ptr, utf8.length); 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); auto aaa = DecodeUTF8(binary, {}, page);
out.resize(aaa.second); out.resize(aaa.second);
@ -70,4 +72,74 @@ namespace Aurora::Locale::Encoding
out.resize(ret.second); out.resize(ret.second);
return ret; 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);
}
} }

View File

@ -7,117 +7,29 @@
***/ ***/
#pragma once #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 namespace Aurora::Locale::Encoding
{ {
AuStreamReadWrittenPair_t DecodeUTF8(void *binary, AuUInt32 binaryLength, AuString &out, ECodePage page = ECodePage::eUnsupported); AuStreamReadWrittenPair_t DecodeUTF8(void *binary, AuUInt32 binaryLength, AuString &out, ECodePage page = ECodePage::eUnsupported);
template <bool optimized = false> struct TextStreamProcessor
struct TextStreamDecoderImpl
{ {
bool readHeader {}; bool readHeader {};
ECodePage page = ECodePage::eUnsupported; ECodePage page = ECodePage::eUnsupported;
ECodePage defaultPage = ECodePage::eUnsupported; ECodePage defaultPage = ECodePage::eUnsupported;
EncoderAdapter state; 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 TypeIn_t = const void *;
using TypeCast_t = std::conditional_t<optimized, AuUInt8 *, const AuUInt8 *>; using TypeCast_t = const AuUInt8 *;
AuStreamReadWrittenPair_t EncodeUTF8(const void *binary, AuUInt32 binaryLength, void *utf8, AuUInt32 utfLen)
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)
{ {
int offset = 0; int offset = 0;
@ -128,11 +40,11 @@ namespace Aurora::Locale::Encoding
{ {
if (page == ECodePage::eUnsupported) if (page == ECodePage::eUnsupported)
{ {
auto header = DecodeBOM(Aurora::Memory::MemoryViewRead(binary, binaryLength)); auto header = DecodeBOM(Memory::MemoryViewRead(binary, binaryLength));
if (header) if (header.length)
{ {
page = header->first; page = header.page;
offset = header->second; offset = header.length;
} }
else else
{ {
@ -155,29 +67,11 @@ namespace Aurora::Locale::Encoding
} }
binaryLength = binaryLength - offset; 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); auto real = state.CPToUTF8(reinterpret_cast<TypeCast_t>(binary) + offset, binaryLength, utf8, utfLen);
return AuMakePair(real.first + offset, real.second); 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); auto preemptive = EncodeUTF8(binary, binaryLength, nullptr, 0);
if (!AuTryResize(out, preemptive.second)) return {}; if (!AuTryResize(out, preemptive.second)) return {};
@ -237,6 +131,4 @@ namespace Aurora::Locale::Encoding
/// 'TextStreamProcessor', a stateful wrapper around DecodeUTF8 /// 'TextStreamProcessor', a stateful wrapper around DecodeUTF8
/// Using this you can handle a stateful, optionally bom prefixed, stream /// Using this you can handle a stateful, optionally bom prefixed, stream
/// Initialization (ie: setting a default codepage) is optional /// Initialization (ie: setting a default codepage) is optional
using TextStreamProcessor = typename TextStreamDecoderImpl<false>;
using TextStreamProcessorInternal = typename TextStreamDecoderImpl<true>;
} }

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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
{
}

View File

@ -345,7 +345,7 @@ namespace Aurora::Locale
gCountryCode = AuToUpper(gCountryCode); gCountryCode = AuToUpper(gCountryCode);
gCodeset = AuToUpper(gCodeset); 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; static bool gLockLocale = false;

View File

@ -51,7 +51,7 @@ namespace Aurora::Memory
class InternalHeap : public Heap class InternalHeap : public Heap
{ {
public: public:
InternalHeap() : base_(nullptr), mutex_(nullptr), heap_(nullptr), _count(0) InternalHeap() : base_(nullptr), mutex_(nullptr), heap_(nullptr), count_(0)
{ } { }
~InternalHeap(); ~InternalHeap();
@ -90,14 +90,14 @@ namespace Aurora::Memory
AuThreadPrimitives::MutexUnique_t mutex_; AuThreadPrimitives::MutexUnique_t mutex_;
void *base_ {}; void *base_ {};
O1HeapInstance *heap_ {}; O1HeapInstance *heap_ {};
int _count {}; int count_ {};
AuUInt length_; AuUInt length_ {};
bool _isDangling {}; bool isDangling_ {};
}; };
InternalHeap::~InternalHeap() InternalHeap::~InternalHeap()
{ {
SysAssertDbgExp(_count == 0); SysAssertDbgExp(count_ == 0);
if (base_) if (base_)
{ {
@ -149,7 +149,7 @@ namespace Aurora::Memory
{ {
if (!heap_) return nullptr; if (!heap_) return nullptr;
auto ret = o1heapAllocate(heap_, length); auto ret = o1heapAllocate(heap_, length);
if (ret) _count++; if (ret) count_++;
return ret; return ret;
} }
@ -210,14 +210,14 @@ namespace Aurora::Memory
{ {
if (buffer == nullptr) return; if (buffer == nullptr) return;
o1heapFree(heap_, buffer); o1heapFree(heap_, buffer);
_count--; count_--;
} }
void InternalHeap::TryRelease() void InternalHeap::TryRelease()
{ {
if (!_isDangling) return; if (!isDangling_) return;
if (_count == 0) if (count_ == 0)
{ {
delete this; delete this;
return; return;
@ -226,13 +226,13 @@ namespace Aurora::Memory
void InternalHeap::RequestTermination() void InternalHeap::RequestTermination()
{ {
if (_count) if (count_)
{ {
LogWarn("Heap life was less than its allocations, waiting for final free"); LogWarn("Heap life was less than its allocations, waiting for final free");
LogWarn("Reporting using mayday!"); LogWarn("Reporting using mayday!");
Telemetry::Mayday(); Telemetry::Mayday();
_isDangling = true; isDangling_ = true;
TryRelease(); TryRelease();
} }
else else

View File

@ -21,9 +21,9 @@ namespace Aurora::Process
exit(exitcode); exit(exitcode);
#else #else
// ??? // ???
*(AuUInt8 *)0 = 0; *(AuUInt32 *)0 = exitcode;
*(AuUInt8 *)0xFFFF = 0; *(AuUInt32 *)0xFFFF = exitcode;
*(AuUInt8 *)0xFFFFFFFF = 0; *(AuUInt32 *)0xFFFFFFFF = exitcode;
#endif #endif
} }
} }

View File

@ -11,6 +11,7 @@
#include "Open.Linux.hpp" #include "Open.Linux.hpp"
#include <unistd.h> #include <unistd.h>
#include <Source/IO/FS/FS.hpp>
namespace Aurora::Processes namespace Aurora::Processes
{ {
@ -30,6 +31,6 @@ namespace Aurora::Processes
AUKN_SYM void OpenFile(const AuString &file) AUKN_SYM void OpenFile(const AuString &file)
{ {
LinuxOpenAsync(file); LinuxOpenAsync(IO::FS::NormalizePathRet(file));
} }
} }

View File

@ -21,7 +21,7 @@ namespace Aurora::Processes
static AuList<AuString> gOpenItems; static AuList<AuString> gOpenItems;
static AuThreadPrimitives::ConditionMutexUnique_t gCondMutex; static AuThreadPrimitives::ConditionMutexUnique_t gCondMutex;
static AuThreadPrimitives::ConditionVariableUnique_t gCondVariable; static AuThreadPrimitives::ConditionVariableUnique_t gCondVariable;
static AuThreading::Threads::ThreadUnique_t gOpenerThread; static AuThreads::ThreadUnique_t gOpenerThread;
static void RunTasks() static void RunTasks()
{ {
@ -49,9 +49,9 @@ namespace Aurora::Processes
{ {
gCondMutex = AuThreadPrimitives::ConditionMutexUnique(); gCondMutex = AuThreadPrimitives::ConditionMutexUnique();
gCondVariable = AuThreadPrimitives::ConditionVariableUnique(AuUnsafeRaiiToShared(gCondMutex)); gCondVariable = AuThreadPrimitives::ConditionVariableUnique(AuUnsafeRaiiToShared(gCondMutex));
gOpenerThread = AuThreading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo( gOpenerThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(OpenerThread)), AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(OpenerThread)),
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}), AuThreads::IThreadVectorsFunctional::OnExit_t{}),
"COM ShellExecute Runner" "COM ShellExecute Runner"
)); ));
gOpenerThread->Run(); gOpenerThread->Run();

View File

@ -33,6 +33,9 @@ namespace Aurora::Processes
} }
} }
// TODO: where the hell is my release
class ProcessImpl : public IProcess class ProcessImpl : public IProcess
{ {
public: public:
@ -53,6 +56,7 @@ namespace Aurora::Processes
bool Read(bool error, void *buffer, AuUInt32 &len) override; bool Read(bool error, void *buffer, AuUInt32 &len) override;
bool Write(const 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; bool Start(enum ESpawnType type, bool fwdOut, bool fwdErr, bool fwdIn) override;
private: private:
@ -71,9 +75,9 @@ namespace Aurora::Processes
AuList<const char *> cargs_; AuList<const char *> cargs_;
AuString windowsCli_; AuString windowsCli_;
Threading::Threads::ThreadUnique_t thread_; AuThreads::ThreadUnique_t thread_;
HANDLE process_ {INVALID_HANDLE_VALUE};; HANDLE process_ {INVALID_HANDLE_VALUE};
HANDLE hthread_ {INVALID_HANDLE_VALUE};; HANDLE hthread_ {INVALID_HANDLE_VALUE};
AuSInt exitCode_; AuSInt exitCode_;
}; };
@ -327,9 +331,9 @@ namespace Aurora::Processes
this->exitCode_ = exitCode; this->exitCode_ = exitCode;
}; };
this->thread_ = AuThreading::Threads::ThreadUnique(AuThreading::Threads::ThreadInfo( this->thread_ = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreading::Threads::IThreadVectorsFunctional>(AuThreading::Threads::IThreadVectorsFunctional::OnEntry_t(std::bind(a)), AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(a)),
AuThreading::Threads::IThreadVectorsFunctional::OnExit_t{}) AuThreads::IThreadVectorsFunctional::OnExit_t{})
)); ));
if (!this->thread_) if (!this->thread_)

View File

@ -58,9 +58,16 @@ namespace Aurora::Threading::Threads
OSThread::~OSThread() OSThread::~OSThread()
{ {
bool bDetached {};
bool bDetachedSuccess {};
if (contextUsed_) if (contextUsed_)
{ {
if (!detached_) if (detached_)
{
bDetached = true;
}
else
{ {
Exit(); Exit();
WaitFor(terminated_.get()); WaitFor(terminated_.get());
@ -69,7 +76,25 @@ namespace Aurora::Threading::Threads
terminated_.reset(); terminated_.reset();
FreeOSContext(); 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 // 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 // TODO: mutex
if (!feature)
{
return;
}
AuTryInsert(threadFeatures_, feature); AuTryInsert(threadFeatures_, feature);
} }
@ -248,13 +277,14 @@ namespace Aurora::Threading::Threads
void OSThread::SetAffinity(AuUInt32 mask) void OSThread::SetAffinity(AuUInt32 mask)
{ {
if (mask == 0) mask = 0xFFFFFFFF;
affinityProcessMask_ = mask; affinityProcessMask_ = mask;
UpdateAffinity(mask); UpdateAffinity(mask);
} }
void OSThread::SetPrio(EThreadPrio prio) void OSThread::SetPrio(EThreadPrio prio)
{ {
prio_ = prio; if (!EThreadPrioIsValid(prio)) return;
UpdatePrio(prio); UpdatePrio(prio);
} }
@ -292,7 +322,7 @@ namespace Aurora::Threading::Threads
return 0; 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) if (ok == -1L)
{ {
SysPushErrorGen("Couldn't initialize thread: {}", GetName()); SysPushErrorGen("Couldn't initialize thread: {}", GetName());
@ -318,9 +348,9 @@ namespace Aurora::Threading::Threads
return false; 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) if (ret != 0)
{ {
SysPushErrorGen("Couldn't create thread: {}", GetName()); SysPushErrorGen("Couldn't create thread: {}", GetName());
@ -346,6 +376,11 @@ namespace Aurora::Threading::Threads
void OSThread::_ThreadEP() 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(); OSAttach();
task_(); task_();
Exit(true); Exit(true);
@ -449,7 +484,7 @@ namespace Aurora::Threading::Threads
this->unixThreadId_ = 0; // !!!! this->unixThreadId_ = 0; // !!!!
#endif #endif
UpdatePrio(prio_); UpdatePrio(prio_);
UpdateAffinity(affinityProcessMask_); SetAffinity(affinityProcessMask_);
UpdateName(); UpdateName();
} }
@ -529,6 +564,19 @@ namespace Aurora::Threading::Threads
// fall through on error // 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, &param);
}
const int *val; const int *val;
if (!AuTryFind(kNiceMap, prio, val)) if (!AuTryFind(kNiceMap, prio, val))
@ -540,7 +588,9 @@ namespace Aurora::Threading::Threads
{ {
setpriority(PRIO_PROCESS, tid, *val); setpriority(PRIO_PROCESS, tid, *val);
} }
#endif #endif
prio_ = prio;
} }
void OSThread::UpdateAffinity(AuUInt32 mask) void OSThread::UpdateAffinity(AuUInt32 mask)

View File

@ -62,6 +62,7 @@ namespace Aurora::Threading::Threads
private: private:
Primitives::SpinLock tlsLock_; Primitives::SpinLock tlsLock_;
AuSPtr<TLSView> tls_; AuSPtr<TLSView> tls_;
OSThread * tlsReferenceThread_ {};
AuString name_; AuString name_;
ThreadInfo info_; ThreadInfo info_;
AuUInt32 affinityProcessMask_ = 0xFFFFFFFF; AuUInt32 affinityProcessMask_ = 0xFFFFFFFF;