From bdec6ff8bac63e87c9397e4c37d0a866f348879c Mon Sep 17 00:00:00 2001 From: Jamie Reece Wilson Date: Mon, 10 Jul 2023 16:29:38 +0100 Subject: [PATCH] [+] AuProcess::EnvironmentGetAll [+] AuProcess::EnvironmentGetOne [+] AuProcess::EnvironmentSetOne [+] AuProcess::EnvironmentRemoveOne [+] AuProcess::EnvironmentRemoveMany [+] AuProcess::EnvironmentSetMany [+] AuProcess::GetProcessStartupTimeNS [+] AuProcess::GetProcessStartupTimeMS [*] Note WakeOnAddress on all platforms [*] Updated READMEs --- Include/Aurora/Process/Process.hpp | 2 + Include/Aurora/Process/ProcessEnvironment.hpp | 38 +++++ Include/Aurora/Process/ProcessStartTime.hpp | 21 +++ Include/Aurora/Threading/README.md | 4 +- README.md | 5 +- Source/Process/AuProcessEnvironment.NT.cpp | 132 ++++++++++++++++++ Source/Process/AuProcessEnvironment.NT.hpp | 13 ++ Source/Process/AuProcessEnvironment.Unix.cpp | 71 ++++++++++ Source/Process/AuProcessEnvironment.Unix.hpp | 13 ++ Source/Process/AuProcessEnvironment.cpp | 63 +++++++++ Source/Process/AuProcessEnvironment.hpp | 13 ++ Source/Process/AuProcessStartTime.cpp | 40 ++++++ Source/Process/AuProcessStartTime.hpp | 13 ++ 13 files changed, 425 insertions(+), 3 deletions(-) create mode 100644 Include/Aurora/Process/ProcessEnvironment.hpp create mode 100644 Include/Aurora/Process/ProcessStartTime.hpp create mode 100644 Source/Process/AuProcessEnvironment.NT.cpp create mode 100644 Source/Process/AuProcessEnvironment.NT.hpp create mode 100644 Source/Process/AuProcessEnvironment.Unix.cpp create mode 100644 Source/Process/AuProcessEnvironment.Unix.hpp create mode 100644 Source/Process/AuProcessEnvironment.cpp create mode 100644 Source/Process/AuProcessEnvironment.hpp create mode 100644 Source/Process/AuProcessStartTime.cpp create mode 100644 Source/Process/AuProcessStartTime.hpp diff --git a/Include/Aurora/Process/Process.hpp b/Include/Aurora/Process/Process.hpp index 698844c5..1b344644 100644 --- a/Include/Aurora/Process/Process.hpp +++ b/Include/Aurora/Process/Process.hpp @@ -11,6 +11,8 @@ #include "ProcessMap.hpp" #include "IProcessSectionMapView.hpp" #include "IProcessSectionView.hpp" +#include "ProcessEnvironment.hpp" +#include "ProcessStartTime.hpp" namespace Aurora::Process { diff --git a/Include/Aurora/Process/ProcessEnvironment.hpp b/Include/Aurora/Process/ProcessEnvironment.hpp new file mode 100644 index 00000000..b633830c --- /dev/null +++ b/Include/Aurora/Process/ProcessEnvironment.hpp @@ -0,0 +1,38 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ProcessEnvironment.hpp + Date: 2023-7-10 + Author: Reece + + Note: I can't stand environment variables. + They're leftover POSIX shitstains used for configuration during process initialization of yesteryear (muh 'stdio/out ipc all the tiny things to build big things' model of computer operating systems). + The underlying environ block as described by the POSIX specifications is inherently thread unsafe, and hasn't carried itself into the modern era of computing past its' usage for shell $%PATH$% expansion... + Well, maybe there's software in the UNIX ecosystem glued together with XDG_HOPES_AND_DREAMS. I try not to think about those. + Well, maybe I'm forgetting the primary user of env-vars... JAVA_HOME!!! I suppose we can't forget about those few Java programs that don't ship the JRE & have startup.[sh/bat] scripts. + + Alternatives: + * Win32 users are better served by the Windows registry + * Linux drivers have module_param to avoid early file io + * NT drivers can query the registry via RtlXXXXX APIs + * Userland IPC identification between child processes can be served by AuCmdLine + * Unix services are a mess of non-standard configuration files + Depending on the use case and OS, early application configuration on modern Unix (^) will differ + ^: IE: A MacOS application package, a Linux server program with nonstandard configs+yamls, or BSD systems with xml interfaces will all want different IO + * Sqlite for large application configuration stores + + Warning: + Aurora Runtime, in a bid to support environment variables properly in userland (ie: not emulating the api with no real effects in real-time), does not buffer the block + Other code in a UNIX process can bypass the mutex required for that respective platform +***/ +#pragma once + +namespace Aurora::Process +{ + AUKN_SYM AuList> EnvironmentGetAll(); + AUKN_SYM AuOptional EnvironmentGetOne(const AuString &key); + AUKN_SYM bool EnvironmentSetOne(const AuString &key, const AuString &value); + AUKN_SYM bool EnvironmentRemoveOne(const AuString &key); + AUKN_SYM bool EnvironmentRemoveMany(const AuList &list); + AUKN_SYM bool EnvironmentSetMany(const AuList> &list); +} \ No newline at end of file diff --git a/Include/Aurora/Process/ProcessStartTime.hpp b/Include/Aurora/Process/ProcessStartTime.hpp new file mode 100644 index 00000000..6c5a9c46 --- /dev/null +++ b/Include/Aurora/Process/ProcessStartTime.hpp @@ -0,0 +1,21 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ProcessStartTime.hpp + Date: 2023-7-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + /** + * @brief Defer to the AuTime namespace to convert the return unsigned integer to something useful + */ + AUKN_SYM AuUInt64 GetProcessStartupTimeNS(); + + /** + * @brief Defer to the AuTime namespace to convert the return unsigned integer to something useful + */ + AUKN_SYM AuUInt64 GetProcessStartupTimeMS(); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/README.md b/Include/Aurora/Threading/README.md index e3f84107..03303857 100644 --- a/Include/Aurora/Threading/README.md +++ b/Include/Aurora/Threading/README.md @@ -3,8 +3,10 @@ # Features * Thread synchronization primitives: condition ex, condition mutex, condition variable, critical section, event, mutex, rwlock, semaphore, and spinlock -* All primitives include a trylock and a timed lock member +* All primitives include a trylock and a nanosecond-resolution timed lock methods * SleepNs function supporting higher resolution sleep available than in most APIs (^1) +* Enchanced Win32 support with increased yield resolution and optimization around internal XP, internal Win8, and UWP APIs +* WakeOnAddress support on every operating system regardless of kernel support * Thread Object per native OS thread * Thread spawning with TLS handles and shutdown routines dispatched under pseudo fibers * Update thread affinity, throttle, name, and related attributes diff --git a/README.md b/README.md index 22587f53..6a79bf2e 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,13 @@ pipeline to get started. - Crypto ECC/[25519, P-384, P-256], [AES, RSA, X509], CBC[AES, Stinky3DES], HMAC, HashCash, BCrypt, [common digests] - Basic cmdline parsing from any module - Exit and fatal save condition callbacks -- Random; secure, user-seeded, and fast +- Random; secure and fast user-seeded backends - Hardware Info; memory and cpu info (including cpu feature bits, core topology, e-core awareness, and basic cache size) - Software stack information for retrieving kernel, version, brand, family, build string, etc -- Compression (deflate, gzip, zstd, LZ4, bzip2, TODO: lzma, brotli) +- Compression (deflate, gzip, zstd, LZ4, bzip2, lzma, brotli) - Locale and encoding - High performance threading and synchronization primitives (os userland sched optimized) +- Alternative WakeOnAddress implementation for non-aurt/user synchronization primitives when kernel support is missing (polyfill when faced with old and/or locked down private APIs) - Async subsystem backed by high performance sync primitives (cv loop) and hybrid switching into IO polling (think userland cv-backed promises + waitmultipleobjects) - IO subsystem for standard cross-platform IO loop queues, IPC (mutex with auto-unlock, semaphores, full-duplex single-connection pipes, and shared memory), file (direct uncached access), and network - Abstract kernel IO transactions, IPC objects, timers, semaphores, and others in the form of ILoopSources diff --git a/Source/Process/AuProcessEnvironment.NT.cpp b/Source/Process/AuProcessEnvironment.NT.cpp new file mode 100644 index 00000000..3cc69e85 --- /dev/null +++ b/Source/Process/AuProcessEnvironment.NT.cpp @@ -0,0 +1,132 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessEnvironment.NT.cpp + Date: 2023-7-10 + Author: Reece +***/ +#include +#include "AuProcessEnvironment.hpp" +#include "AuProcessEnvironment.NT.hpp" + +namespace Aurora::Process +{ + AUKN_SYM AuList> EnvironmentGetAll() + { + AuList> ret; + + auto pWideStrings = ::GetEnvironmentStringsW(); + auto pFreeBase = pWideStrings; + + while (auto uLength = ::wcslen(pWideStrings)) + { + auto utfString = AuLocale::ConvertFromWChar(pWideStrings, uLength); + + if (utfString.empty()) + { + ::FreeEnvironmentStringsW(pFreeBase); + SysPushErrorNested(); + return {}; + } + + try + { + auto optSplit = EnvironmentSplitString(utfString); + if (optSplit) + { + ret.push_back(optSplit.value()); + } + } + catch (...) + { + ::FreeEnvironmentStringsW(pFreeBase); + SysPushErrorCatch(); + return {}; + } + + + pWideStrings += uLength; + pWideStrings += 1; + } + + ::FreeEnvironmentStringsW(pFreeBase); + + return ret; + } + + AUKN_SYM AuOptional EnvironmentGetOne(const AuString &key) + { + auto keyString = AuLocale::ConvertFromUTF8(key); + + if (keyString.empty()) + { + return {}; + } + + auto uLength = ::GetEnvironmentVariableW(keyString.c_str(), + nullptr, + 0); + + if (uLength == 0) + { + if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) + { + SysPushErrorResourceMissing(); + return {}; + } + else + { + return ""; + } + } + + std::wstring temp; + + if (!AuTryResize(temp, uLength)) + { + SysPushErrorMemory(); + return {}; + } + + uLength = ::GetEnvironmentVariableW(keyString.c_str(), + temp.data(), + temp.size()); + + if (!uLength) + { + SysPushErrorGeneric(); + return {}; + } + + temp.resize(uLength); + return AuLocale::ConvertFromWChar(temp.c_str()); + } + + AUKN_SYM bool EnvironmentSetOne(const AuString &key, const AuString &value) + { + auto keyString = AuLocale::ConvertFromUTF8(key); + auto valString = AuLocale::ConvertFromUTF8(value); + + if (keyString.empty() || valString.empty()) + { + return false; + } + + return ::SetEnvironmentVariableW(keyString.c_str(), + valString.c_str()); + } + + AUKN_SYM bool EnvironmentRemoveOne(const AuString &key) + { + std::wstring keyString; + + if ((keyString = AuLocale::ConvertFromUTF8(key)).size()) + { + return ::SetEnvironmentVariableW(keyString.c_str(), nullptr); + } + else + { + return false; + } + } +} \ No newline at end of file diff --git a/Source/Process/AuProcessEnvironment.NT.hpp b/Source/Process/AuProcessEnvironment.NT.hpp new file mode 100644 index 00000000..00208d8c --- /dev/null +++ b/Source/Process/AuProcessEnvironment.NT.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessEnvironment.NT.hpp + Date: 2023-7-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + +} \ No newline at end of file diff --git a/Source/Process/AuProcessEnvironment.Unix.cpp b/Source/Process/AuProcessEnvironment.Unix.cpp new file mode 100644 index 00000000..0291aa3e --- /dev/null +++ b/Source/Process/AuProcessEnvironment.Unix.cpp @@ -0,0 +1,71 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessEnvironment.Unix.cpp + Date: 2023-7-10 + Author: Reece +***/ +#include +#include "AuProcessEnvironment.hpp" +#include "AuProcessEnvironment.Unix.hpp" +#include + +namespace Aurora::Process +{ + static AuThreadPrimitives::Mutex gEnvMutex; + + AUKN_SYM AuList> EnvironmentGetAll() + { + AU_LOCK_GUARD(gEnvMutex); + AuList> ret; + + auto pIterator = environ; + while (auto pCurrent = *(pIterator++)) + { + auto optSplit = EnvironmentSplitString(pCurrent); + if (optSplit) + { + ret.push_back(optSplit.value()); + } + } + + return ret; + } + + AUKN_SYM AuOptional EnvironmentGetOne(const AuString &key) + { + AU_LOCK_GUARD(gEnvMutex); + + if (key.empty()) + { + return false; + } + + auto pValue = ::getenv(key.c_str()); + return pValue ? AuString(pValue) : AuOptional {}; + } + + AUKN_SYM bool EnvironmentSetOne(const AuString &key, const AuString &value) + { + AU_LOCK_GUARD(gEnvMutex); + + if (key.empty()) + { + return false; + } + + return ::setenv(key.c_str(), value.c_str(), 1) == 0; + } + + AUKN_SYM bool EnvironmentRemoveOne(const AuString &key) + { + AU_LOCK_GUARD(gEnvMutex); + + if (key.empty()) + { + return false; + } + + return ::unsetenv(key.c_str()) == 0; + } +} \ No newline at end of file diff --git a/Source/Process/AuProcessEnvironment.Unix.hpp b/Source/Process/AuProcessEnvironment.Unix.hpp new file mode 100644 index 00000000..e2377bfe --- /dev/null +++ b/Source/Process/AuProcessEnvironment.Unix.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessEnvironment.Unix.hpp + Date: 2023-7-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + +} \ No newline at end of file diff --git a/Source/Process/AuProcessEnvironment.cpp b/Source/Process/AuProcessEnvironment.cpp new file mode 100644 index 00000000..809e7715 --- /dev/null +++ b/Source/Process/AuProcessEnvironment.cpp @@ -0,0 +1,63 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessEnvironment.cpp + Date: 2023-7-10 + Author: Reece +***/ +#include +#include "AuProcessEnvironment.hpp" + +namespace Aurora::Process +{ + AUKN_SYM bool EnvironmentRemoveMany(const AuList &list) + { + bool bFailed {}; + + for (const auto &key : list) + { + if (!EnvironmentRemoveOne(key)) + { + bFailed |= true; + } + } + + return !bFailed; + } + + + AUKN_SYM bool EnvironmentSetMany(const AuList> &list) + { + for (const auto &[key, value] : list) + { + if (!EnvironmentSetOne(key, value)) + { + return false; + } + } + + return true; + } + + AuOptional> EnvironmentSplitString(const AuString &str) + { + auto uIdx = str.find('='); + + if (uIdx == str.npos) + { + return {}; + } + + if (uIdx == 0) + { + // Ignore Microsoft DOS shell environment hints + // Expect to see: + // =::=::\ + // (opt) =:= + // (opt) =ExitCode={:08x} + return {}; + } + + return AuMakePair(str.substr(0, uIdx), str.substr(uIdx + 1)); + } +} \ No newline at end of file diff --git a/Source/Process/AuProcessEnvironment.hpp b/Source/Process/AuProcessEnvironment.hpp new file mode 100644 index 00000000..2331c8cb --- /dev/null +++ b/Source/Process/AuProcessEnvironment.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessEnvironment.hpp + Date: 2023-7-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + AuOptional> EnvironmentSplitString(const AuString &str); +} \ No newline at end of file diff --git a/Source/Process/AuProcessStartTime.cpp b/Source/Process/AuProcessStartTime.cpp new file mode 100644 index 00000000..29c6d64b --- /dev/null +++ b/Source/Process/AuProcessStartTime.cpp @@ -0,0 +1,40 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessStartTime.cpp + Date: 2023-7-10 + Author: Reece +***/ +#include +#include "AuProcessStartTime.hpp" + +namespace Aurora::Process +{ + AUKN_SYM AuUInt64 GetProcessStartupTimeNS() + { + static AuOptional optKnownWallTime; + + if (optKnownWallTime.has_value()) + { + return optKnownWallTime.value(); + } + + auto uUptime = AuTime::ProcessClockNS(); + + if (!uUptime) + { + optKnownWallTime = AuTime::CurrentClockNS(); + } + else + { + optKnownWallTime = AuTime::CurrentClockNS() - uUptime; + } + + return optKnownWallTime.value(); + } + + AUKN_SYM AuUInt64 GetProcessStartupTimeMS() + { + return AuNSToMS(GetProcessStartupTimeNS()); + } +} \ No newline at end of file diff --git a/Source/Process/AuProcessStartTime.hpp b/Source/Process/AuProcessStartTime.hpp new file mode 100644 index 00000000..0069a982 --- /dev/null +++ b/Source/Process/AuProcessStartTime.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessStartTime.hpp + Date: 2023-7-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + +} \ No newline at end of file