diff options
| author | Martin Ridgers <[email protected]> | 2021-09-21 11:06:13 +0200 |
|---|---|---|
| committer | Martin Ridgers <[email protected]> | 2021-09-21 11:06:13 +0200 |
| commit | 68c951e0f440ffd483795dced737e88152c1a581 (patch) | |
| tree | 5c0910ca2a85b45fb05dba3ce457b7d156213894 /zencore/include | |
| parent | Merge main into linux-mac (diff) | |
| parent | Trigger storage scrubbing pass at startup (diff) | |
| download | zen-68c951e0f440ffd483795dced737e88152c1a581.tar.xz zen-68c951e0f440ffd483795dced737e88152c1a581.zip | |
Merged main into linux-mac
Diffstat (limited to 'zencore/include')
| -rw-r--r-- | zencore/include/zencore/except.h | 53 | ||||
| -rw-r--r-- | zencore/include/zencore/logging.h | 7 | ||||
| -rw-r--r-- | zencore/include/zencore/mpscqueue.h | 109 | ||||
| -rw-r--r-- | zencore/include/zencore/session.h | 3 | ||||
| -rw-r--r-- | zencore/include/zencore/snapshot_manifest.h | 57 | ||||
| -rw-r--r-- | zencore/include/zencore/string.h | 39 | ||||
| -rw-r--r-- | zencore/include/zencore/testing.h | 9 | ||||
| -rw-r--r-- | zencore/include/zencore/testutils.h | 31 | ||||
| -rw-r--r-- | zencore/include/zencore/thread.h | 33 | ||||
| -rw-r--r-- | zencore/include/zencore/uid.h | 2 | ||||
| -rw-r--r-- | zencore/include/zencore/zencore.h | 94 |
11 files changed, 302 insertions, 135 deletions
diff --git a/zencore/include/zencore/except.h b/zencore/include/zencore/except.h index f0e04a795..1dc6209d6 100644 --- a/zencore/include/zencore/except.h +++ b/zencore/include/zencore/except.h @@ -15,63 +15,20 @@ namespace zen { #if ZEN_PLATFORM_WINDOWS -class WindowsException : public std::exception -{ -public: - WindowsException(std::string_view Message) - { - m_hResult = HRESULT_FROM_WIN32(GetLastError()); - m_Message = Message; - } - - WindowsException(HRESULT hRes, std::string_view Message) - { - m_hResult = hRes; - m_Message = Message; - } - - WindowsException(HRESULT hRes, const char* Message, const char* Detail) - { - m_hResult = hRes; - - ExtendableStringBuilder<128> msg; - msg.Append(Message); - msg.Append(" (detail: '"); - msg.Append(Detail); - msg.Append("')"); - - m_Message = msg.c_str(); - } - - virtual const char* what() const override { return m_Message.c_str(); } - -private: - std::string m_Message; - HRESULT m_hResult; -}; - -ZENCORE_API void ThrowSystemException(HRESULT hRes, std::string_view Message); +ZENCORE_API void ThrowSystemException [[noreturn]] (HRESULT hRes, std::string_view Message); #endif // ZEN_PLATFORM_WINDOWS -ZENCORE_API void ThrowLastError(std::string_view Message); +ZENCORE_API void ThrowLastError [[noreturn]] (std::string_view Message); #if __cpp_lib_source_location -ZENCORE_API void ThrowLastError(std::string_view Message, const std::source_location& Location); +ZENCORE_API void ThrowLastError [[noreturn]] (std::string_view Message, const std::source_location& Location); #endif +ZENCORE_API void ThrowSystemError [[noreturn]] (uint32_t ErrorCode, std::string_view Message); + ZENCORE_API std::string GetLastErrorAsString(); ZENCORE_API std::string GetErrorAsString(uint32_t ErrorCode); -inline void -ThrowSystemException(const char* Message) -{ -#if ZEN_PLATFORM_WINDOWS - throw WindowsException(Message); -#else - ThrowLastError(Message); -#endif -} - inline int32_t GetLastError() { diff --git a/zencore/include/zencore/logging.h b/zencore/include/zencore/logging.h index 412a39415..221f5f358 100644 --- a/zencore/include/zencore/logging.h +++ b/zencore/include/zencore/logging.h @@ -80,3 +80,10 @@ using zen::Log; using namespace std::literals; \ Log().critical(fmtstr##sv, ##__VA_ARGS__);\ } while (false) + +#define ZEN_CONSOLE(fmtstr, ...) \ + do \ + { \ + using namespace std::literals; \ + ConsoleLog().info(fmtstr##sv, __VA_ARGS__); \ + } while (false) diff --git a/zencore/include/zencore/mpscqueue.h b/zencore/include/zencore/mpscqueue.h new file mode 100644 index 000000000..bb558bb5a --- /dev/null +++ b/zencore/include/zencore/mpscqueue.h @@ -0,0 +1,109 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <atomic> +#include <new> +#include <optional> + +#ifdef __cpp_lib_hardware_interference_size +using std::hardware_constructive_interference_size; +using std::hardware_destructive_interference_size; +#else +// 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... +constexpr std::size_t hardware_constructive_interference_size = 64; +constexpr std::size_t hardware_destructive_interference_size = 64; +#endif + +namespace zen { + +/** An untyped array of data with compile-time alignment and size derived from another type. */ +template<typename ElementType> +struct TypeCompatibleStorage +{ + ElementType* Data() { return (ElementType*)this; } + const ElementType* Data() const { return (const ElementType*)this; } + + char alignas(ElementType) DataMember; +}; + +/** Fast multi-producer/single-consumer unbounded concurrent queue. + + Based on http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue + */ + +template<typename T> +class MpscQueue final +{ +public: + using ElementType = T; + + MpscQueue() + { + Node* Sentinel = new Node; + Head.store(Sentinel, std::memory_order_relaxed); + Tail = Sentinel; + } + + ~MpscQueue() + { + Node* Next = Tail->Next.load(std::memory_order_relaxed); + + // sentinel's value is already destroyed + delete Tail; + + while (Next != nullptr) + { + Tail = Next; + Next = Tail->Next.load(std::memory_order_relaxed); + + std::destroy_at((ElementType*)&Tail->Value); + delete Tail; + } + } + + template<typename... ArgTypes> + void Enqueue(ArgTypes&&... Args) + { + Node* New = new Node; + new (&New->Value) ElementType(std::forward<ArgTypes>(Args)...); + + Node* Prev = Head.exchange(New, std::memory_order_acq_rel); + Prev->Next.store(New, std::memory_order_release); + } + + std::optional<ElementType> Dequeue() + { + Node* Next = Tail->Next.load(std::memory_order_acquire); + + if (Next == nullptr) + { + return {}; + } + + ElementType* ValuePtr = (ElementType*)&Next->Value; + std::optional<ElementType> Res{std::move(*ValuePtr)}; + std::destroy_at(ValuePtr); + + delete Tail; // current sentinel + + Tail = Next; // new sentinel + return Res; + } + +private: + struct Node + { + std::atomic<Node*> Next{nullptr}; + TypeCompatibleStorage<ElementType> Value; + }; + +private: + std::atomic<Node*> Head; // accessed only by producers + alignas(hardware_constructive_interference_size) + Node* Tail; // accessed only by consumer, hence should be on a different cache line than `Head` +}; + +void mpscqueue_forcelink(); + +} // namespace zen diff --git a/zencore/include/zencore/session.h b/zencore/include/zencore/session.h index 2da41b2c8..dd90197bf 100644 --- a/zencore/include/zencore/session.h +++ b/zencore/include/zencore/session.h @@ -8,6 +8,7 @@ namespace zen { struct Oid; -ZENCORE_API Oid GetSessionId(); +ZENCORE_API [[nodiscard]] Oid GetSessionId(); +ZENCORE_API [[nodiscard]] std::string_view GetSessionIdString(); } // namespace zen diff --git a/zencore/include/zencore/snapshot_manifest.h b/zencore/include/zencore/snapshot_manifest.h deleted file mode 100644 index 95e64773a..000000000 --- a/zencore/include/zencore/snapshot_manifest.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include <zencore/iohash.h> -#include <zencore/zencore.h> - -#include <filesystem> -#include <functional> -#include <string> -#include <vector> - -namespace zen { - -struct LeafNode -{ - uint64_t FileSize = 0; - uint64_t FileModifiedTime = 0; - zen::IoHash ChunkHash = zen::IoHash::Zero; - std::wstring Name; -}; - -struct TreeNode -{ - std::vector<TreeNode> Children; - std::vector<LeafNode> Leaves; - std::wstring Name; - zen::BLAKE3 ChunkHash = zen::BLAKE3::Zero; - - ZENCORE_API void VisitModifyFiles(std::function<void(LeafNode& node)> func); - ZENCORE_API void VisitFiles(std::function<void(const LeafNode& node)> func); - ZENCORE_API void Finalize(); -}; - -struct SnapshotManifest -{ - std::string Id; - TreeNode Root; - zen::BLAKE3 ChunkHash = zen::BLAKE3::Zero; - - ZENCORE_API void finalize(); -}; - -class InStream; -class OutStream; - -ZENCORE_API void ReadManifest(SnapshotManifest& Manifest, InStream& FromStream); -ZENCORE_API void WriteManifest(const SnapshotManifest& Manifest, OutStream& ToStream); -ZENCORE_API void PrintManifest(const SnapshotManifest& Manifest, OutStream& ToStream); - -// Translate a user-provided manifest specification into a file path. -// Supports hashtag syntax to implicitly refer to user documents zenfs folder -ZENCORE_API std::filesystem::path ManifestSpecToPath(const char* ManifestSpec); - -void snapshotmanifest_forcelink(); - -} // namespace zen diff --git a/zencore/include/zencore/string.h b/zencore/include/zencore/string.h index 2b5f20f86..bb9b1c896 100644 --- a/zencore/include/zencore/string.h +++ b/zencore/include/zencore/string.h @@ -622,6 +622,45 @@ ToLower(const std::string_view& InString) ////////////////////////////////////////////////////////////////////////// +template<typename Fn> +uint32_t +ForEachStrTok(const std::string_view& Str, char Delim, Fn&& Func) +{ + auto It = Str.begin(); + auto End = Str.end(); + uint32_t Count = 0; + + while (It != End) + { + if (*It == Delim) + { + It++; + continue; + } + + std::string_view Remaining{It, End}; + size_t Idx = Remaining.find(Delim, 0); + + if (Idx == std::string_view::npos) + { + Idx = Remaining.size(); + } + + Count++; + std::string_view Token{It, It + Idx}; + if (!Func(Token)) + { + break; + } + + It = It + Idx; + } + + return Count; +} + +////////////////////////////////////////////////////////////////////////// + void string_forcelink(); // internal } // namespace zen diff --git a/zencore/include/zencore/testing.h b/zencore/include/zencore/testing.h new file mode 100644 index 000000000..80aebc26e --- /dev/null +++ b/zencore/include/zencore/testing.h @@ -0,0 +1,9 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/zencore.h> + +#if ZEN_WITH_TESTS +# include <doctest/doctest.h> +#endif diff --git a/zencore/include/zencore/testutils.h b/zencore/include/zencore/testutils.h new file mode 100644 index 000000000..72d985d5c --- /dev/null +++ b/zencore/include/zencore/testutils.h @@ -0,0 +1,31 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <filesystem> + +namespace zen { + +std::filesystem::path CreateTemporaryDirectory(); + +class ScopedTemporaryDirectory +{ +public: + ScopedTemporaryDirectory(); + ~ScopedTemporaryDirectory(); + + std::filesystem::path& Path() { return m_RootPath; } + +private: + std::filesystem::path m_RootPath; +}; + +struct ScopedCurrentDirectoryChange +{ + std::filesystem::path OldPath{std::filesystem::current_path()}; + + ScopedCurrentDirectoryChange() { std::filesystem::current_path(CreateTemporaryDirectory()); } + ~ScopedCurrentDirectoryChange() { std::filesystem::current_path(OldPath); } +}; + +} // namespace zen diff --git a/zencore/include/zencore/thread.h b/zencore/include/zencore/thread.h index b18da6031..7889682cd 100644 --- a/zencore/include/zencore/thread.h +++ b/zencore/include/zencore/thread.h @@ -4,9 +4,9 @@ #include "zencore.h" -#if !ZEN_PLATFORM_WINDOWS -# include <shared_mutex> -#endif +#include <shared_mutex> + +#include <vector> namespace zen { @@ -64,11 +64,7 @@ public: }; private: -#if ZEN_PLATFORM_WINDOWS - void* m_Srw = nullptr; -#else std::shared_mutex m_Mutex; -#endif }; /** Basic abstraction of a simple event synchronization mechanism (aka 'binary semaphore') @@ -93,6 +89,7 @@ public: ZENCORE_API void Set(); ZENCORE_API void Reset(); ZENCORE_API bool Wait(int TimeoutMs = -1); + ZENCORE_API void Close(); protected: explicit Event(void* EventHandle) : m_EventHandle(EventHandle) {} @@ -150,6 +147,28 @@ private: int m_Pid = 0; }; +/** Process monitor - monitors a list of running processes via polling + + Intended to be used to monitor a set of "sponsor" processes, where + we need to determine when none of them remain alive + + */ + +class ProcessMonitor +{ +public: + ProcessMonitor(); + ~ProcessMonitor(); + + ZENCORE_API bool IsRunning(); + ZENCORE_API void AddPid(int Pid); + ZENCORE_API bool IsActive() const; + +private: + mutable RwLock m_Lock; + std::vector<void*> m_ProcessHandles; +}; + ZENCORE_API bool IsProcessRunning(int pid); ZENCORE_API int GetCurrentProcessId(); diff --git a/zencore/include/zencore/uid.h b/zencore/include/zencore/uid.h index 2730b1415..f4e9ab65a 100644 --- a/zencore/include/zencore/uid.h +++ b/zencore/include/zencore/uid.h @@ -60,6 +60,8 @@ struct Oid const Oid& Generate(); [[nodiscard]] static Oid FromHexString(const std::string_view String); StringBuilderBase& ToString(StringBuilderBase& OutString) const; + void ToString(char OutString[StringLength]); + [[nodiscard]] static Oid FromMemory(const void* Ptr); auto operator<=>(const Oid& rhs) const = default; [[nodiscard]] inline operator bool() const { return *this == Zero; } diff --git a/zencore/include/zencore/zencore.h b/zencore/include/zencore/zencore.h index 54df7e85e..f6093cb96 100644 --- a/zencore/include/zencore/zencore.h +++ b/zencore/include/zencore/zencore.h @@ -3,9 +3,13 @@ #pragma once #include <cinttypes> -#include <exception> +#include <stdexcept> #include <string> +#ifndef ZEN_WITH_TESTS +# define ZEN_WITH_TESTS 1 +#endif + ////////////////////////////////////////////////////////////////////////// // Platform // @@ -56,6 +60,10 @@ # endif #endif +#if ZEN_COMPILER_MSC +# pragma warning(disable : 4324) // warning C4324: '<type>': structure was padded due to alignment specifier +#endif + ////////////////////////////////////////////////////////////////////////// // Architecture // @@ -90,37 +98,69 @@ // Assert // +#if ZEN_PLATFORM_WINDOWS +// Tells the compiler to put the decorated function in a certain section (aka. segment) of the executable. +# define ZEN_CODE_SECTION(Name) __declspec(code_seg(Name)) +# define ZEN_FORCENOINLINE __declspec(noinline) /* Force code to NOT be inline */ +#else +# define ZEN_CODE_SECTION(Name) +# define ZEN_FORCENOINLINE +#endif + +#if ZEN_ARCH_ARM64 +// On ARM we can't do this because the executable will require jumps larger +// than the branch instruction can handle. Clang will only generate +// the trampolines in the .text segment of the binary. If the uedbg segment +// is present it will generate code that it cannot link. +# define ZEN_DEBUG_SECTION +#else +// We'll put all assert implementation code into a separate section in the linked +// executable. This code should never execute so using a separate section keeps +// it well off the hot path and hopefully out of the instruction cache. It also +// facilitates reasoning about the makeup of a compiled/linked binary. +# define ZEN_DEBUG_SECTION ZEN_CODE_SECTION(".zcold") +#endif // DO_CHECK || DO_GUARD_SLOW + namespace zen { -class AssertException : public std::exception +class AssertException : public std::logic_error { public: - AssertException(const char* Msg); - ~AssertException(); - - [[nodiscard]] virtual char const* what() const noexcept override { return m_Msg.c_str(); } - -private: - std::string m_Msg; + AssertException(const char* Msg) : std::logic_error(Msg) {} }; } // namespace zen -#define ZEN_ASSERT(x, ...) \ - do \ - { \ - if (x) \ - break; \ - throw ::zen::AssertException{#x}; \ +template<typename RetType = void, class InnerType, typename... ArgTypes> +RetType ZEN_FORCENOINLINE ZEN_DEBUG_SECTION +DispatchAssert(InnerType&& Inner, ArgTypes const&... Args) +{ + return Inner(Args...); +} + +#define ZEN_ASSERT(x, ...) \ + do \ + { \ + if (x) [[unlikely]] \ + break; \ + struct Impl \ + { \ + static void ZEN_FORCENOINLINE ZEN_DEBUG_SECTION ExecThrow [[noreturn]] () { throw ::zen::AssertException{#x}; } \ + }; \ + Impl::ExecThrow(); \ } while (false) #ifndef NDEBUG -# define ZEN_ASSERT_SLOW(x, ...) \ - do \ - { \ - if (x) \ - break; \ - throw ::zen::AssertException{#x}; \ +# define ZEN_ASSERT_SLOW(x, ...) \ + do \ + { \ + if (x) [[unlikely]] \ + break; \ + struct Impl \ + { \ + static void ZEN_FORCENOINLINE ZEN_DEBUG_SECTION ExecThrow [[noreturn]] () { throw ::zen::AssertException{#x}; } \ + }; \ + Impl::ExecThrow(); \ } while (false) #else # define ZEN_ASSERT_SLOW(x, ...) @@ -148,14 +188,24 @@ char (&ZenArrayCountHelper(const T (&)[N]))[N + 1]; #define ZEN_UNUSED(...) ((void)__VA_ARGS__) #define ZEN_NOT_IMPLEMENTED(...) ZEN_ASSERT(false, __VA_ARGS__) -#define ZENCORE_API // Placeholder to allow DLL configs in the future +#define ZENCORE_API // Placeholder to allow DLL configs in the future (maybe) + +namespace zen { ZENCORE_API bool IsPointerToStack(const void* ptr); // Query if pointer is within the stack of the currently executing thread ZENCORE_API bool IsApplicationExitRequested(); ZENCORE_API void RequestApplicationExit(int ExitCode); +ZENCORE_API bool IsDebuggerPresent(); +ZENCORE_API bool IsInteractiveSession(); ZENCORE_API void zencore_forcelinktests(); +} // namespace zen + +#ifndef ZEN_USE_MIMALLOC +# define ZEN_USE_MIMALLOC 1 +#endif + ////////////////////////////////////////////////////////////////////////// #if ZEN_COMPILER_MSC |