diff options
Diffstat (limited to 'zencore')
42 files changed, 713 insertions, 485 deletions
diff --git a/zencore/blake3.cpp b/zencore/blake3.cpp index 090eb6897..663f21b6d 100644 --- a/zencore/blake3.cpp +++ b/zencore/blake3.cpp @@ -4,12 +4,12 @@ #include <zencore/compositebuffer.h> #include <zencore/string.h> +#include <zencore/testing.h> #include <zencore/zencore.h> #include "../3rdparty/BLAKE3/c/blake3.h" #pragma comment(lib, "blake3.lib") -#include <doctest/doctest.h> #include <string.h> ////////////////////////////////////////////////////////////////////////// @@ -123,6 +123,8 @@ BLAKE3Stream::GetHash() // Testing related code follows... // +#if ZEN_WITH_TESTS + doctest::String toString(const BLAKE3& value) { @@ -169,4 +171,6 @@ TEST_CASE("BLAKE3") } } +#endif + } // namespace zen diff --git a/zencore/compactbinary.cpp b/zencore/compactbinary.cpp index b508d8fe8..f4908aa9a 100644 --- a/zencore/compactbinary.cpp +++ b/zencore/compactbinary.cpp @@ -2,12 +2,12 @@ #include "zencore/compactbinary.h" +#include <zencore/compactbinaryvalidation.h> #include <zencore/compress.h> #include <zencore/endian.h> #include <zencore/stream.h> -#include "zencore/compactbinaryvalidation.h" +#include <zencore/testing.h> -#include <doctest/doctest.h> #include <string_view> namespace zen { @@ -1146,6 +1146,7 @@ SaveCompactBinary(BinaryWriter& Ar, const CbObjectView& Object) ////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS void uson_forcelink() { @@ -1297,5 +1298,6 @@ TEST_CASE("uson.null") CHECK(Field.IsNull() == false); } } +#endif } // namespace zen diff --git a/zencore/compactbinarybuilder.cpp b/zencore/compactbinarybuilder.cpp index 08f37a23d..fa5b6a69b 100644 --- a/zencore/compactbinarybuilder.cpp +++ b/zencore/compactbinarybuilder.cpp @@ -7,12 +7,11 @@ #include <zencore/endian.h> #include <zencore/stream.h> #include <zencore/string.h> +#include <zencore/testing.h> #define _USE_MATH_DEFINES #include <math.h> -#include <doctest/doctest.h> - namespace zen { template<typename T> @@ -700,6 +699,7 @@ operator<<(CbWriter& Writer, const TimeSpan Value) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS void usonbuilder_forcelink() { @@ -1534,5 +1534,6 @@ TEST_CASE("usonbuilder.stream") CHECK(ValidateCompactBinary(Object.GetBuffer(), CbValidateMode::All) == CbValidateError::None); } } +#endif } // namespace zen diff --git a/zencore/compactbinarypackage.cpp b/zencore/compactbinarypackage.cpp index 9a7e7c098..fbdcd24e9 100644 --- a/zencore/compactbinarypackage.cpp +++ b/zencore/compactbinarypackage.cpp @@ -5,8 +5,7 @@ #include <zencore/compactbinaryvalidation.h> #include <zencore/endian.h> #include <zencore/stream.h> - -#include <doctest/doctest.h> +#include <zencore/testing.h> namespace zen { @@ -747,6 +746,8 @@ namespace legacy { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS + void usonpackage_forcelink() { @@ -1253,4 +1254,6 @@ TEST_CASE("usonpackage.serialization") } } +#endif + } // namespace zen diff --git a/zencore/compactbinaryvalidation.cpp b/zencore/compactbinaryvalidation.cpp index dafd1bcc8..3d72148f9 100644 --- a/zencore/compactbinaryvalidation.cpp +++ b/zencore/compactbinaryvalidation.cpp @@ -6,11 +6,10 @@ #include <zencore/endian.h> #include <zencore/memory.h> #include <zencore/string.h> +#include <zencore/testing.h> #include <algorithm> -#include <doctest/doctest.h> - namespace zen { namespace CbValidationPrivate { @@ -649,6 +648,7 @@ ValidateCompactBinaryPackage(MemoryView View, CbValidateMode Mode) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS void usonvalidation_forcelink() { @@ -658,5 +658,6 @@ TEST_CASE("usonvalidation") { SUBCASE("Basic") {} } +#endif } // namespace zen diff --git a/zencore/compositebuffer.cpp b/zencore/compositebuffer.cpp index 9349c014f..3190ca5ea 100644 --- a/zencore/compositebuffer.cpp +++ b/zencore/compositebuffer.cpp @@ -3,8 +3,7 @@ #include <zencore/compositebuffer.h> #include <zencore/sharedbuffer.h> - -#include <doctest/doctest.h> +#include <zencore/testing.h> namespace zen { @@ -91,8 +90,8 @@ CompositeBuffer CompositeBuffer::Mid(uint64_t Offset, uint64_t Size) const { const uint64_t BufferSize = GetSize(); - Offset = zen::Min(Offset, BufferSize); - Size = zen::Min(Size, BufferSize - Offset); + Offset = Min(Offset, BufferSize); + Size = Min(Size, BufferSize - Offset); CompositeBuffer Buffer; IterateRange(Offset, Size, [&Buffer](MemoryView View, const SharedBuffer& ViewOuter) { Buffer.m_Segments.push_back(SharedBuffer::MakeView(View, ViewOuter)); @@ -168,6 +167,7 @@ CompositeBuffer::IterateRange(uint64_t Offset, } } +#if ZEN_WITH_TESTS TEST_CASE("CompositeBuffer Null") { CompositeBuffer Buffer; @@ -337,5 +337,6 @@ void compositebuffer_forcelink() { } +#endif } // namespace zen diff --git a/zencore/compress.cpp b/zencore/compress.cpp index 12a7b9ef8..8ca799e39 100644 --- a/zencore/compress.cpp +++ b/zencore/compress.cpp @@ -6,13 +6,13 @@ #include <zencore/compositebuffer.h> #include <zencore/crc32.h> #include <zencore/endian.h> +#include <zencore/testing.h> #include "../3rdparty/Oodle/include/oodle2.h" #if ZEN_PLATFORM_WINDOWS # pragma comment(lib, "oo2core_win64.lib") #endif -#include <doctest/doctest.h> #include <lz4.h> #include <functional> #include <limits> @@ -823,6 +823,8 @@ CompressedBuffer::TryGetCompressParameters(OodleCompressor& OutCompressor, Oodle \/ \/ \/ */ +#if ZEN_WITH_TESTS + TEST_CASE("CompressedBuffer") { uint8_t Zeroes[1024]{}; @@ -908,5 +910,6 @@ void compress_forcelink() { } +#endif } // namespace zen diff --git a/zencore/except.cpp b/zencore/except.cpp index 84e52ab9f..0167c406f 100644 --- a/zencore/except.cpp +++ b/zencore/except.cpp @@ -7,6 +7,41 @@ 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; +}; + void ThrowSystemException([[maybe_unused]] HRESULT hRes, [[maybe_unused]] std::string_view Message) { @@ -28,6 +63,12 @@ ThrowLastError(std::string_view Message) throw std::system_error(std::error_code(zen::GetLastError(), std::system_category()), std::string(Message)); } +void +ThrowSystemError(uint32_t ErrorCode, std::string_view Message) +{ + throw std::system_error(std::error_code(ErrorCode, std::system_category()), std::string(Message)); +} + std::string GetLastErrorAsString() { diff --git a/zencore/filesystem.cpp b/zencore/filesystem.cpp index afbddcdbd..8ddcbac52 100644 --- a/zencore/filesystem.cpp +++ b/zencore/filesystem.cpp @@ -458,14 +458,14 @@ WriteFile(std::filesystem::path Path, const IoBuffer* const* Data, size_t Buffer HRESULT hRes = Outfile.Create(Path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS); if (hRes == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) { - zen::CreateDirectories(Path.parent_path()); + CreateDirectories(Path.parent_path()); hRes = Outfile.Create(Path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS); } if (FAILED(hRes)) { - zen::ThrowSystemException(hRes, "File open failed for '{}'"_format(Path).c_str()); + ThrowSystemException(hRes, "File open failed for '{}'"_format(Path).c_str()); } #else @@ -491,13 +491,13 @@ WriteFile(std::filesystem::path Path, const IoBuffer* const* Data, size_t Buffer while (WriteSize) { - const uint64_t ChunkSize = zen::Min<uint64_t>(WriteSize, uint64_t(2) * 1024 * 1024 * 1024); + const uint64_t ChunkSize = Min<uint64_t>(WriteSize, uint64_t(2) * 1024 * 1024 * 1024); #if ZEN_PLATFORM_WINDOWS hRes = Outfile.Write(DataPtr, gsl::narrow_cast<uint32_t>(WriteSize)); if (FAILED(hRes)) { - zen::ThrowSystemException(hRes, "File write failed for '{}'"_format(Path).c_str()); + ThrowSystemException(hRes, "File write failed for '{}'"_format(Path).c_str()); } #else if (write(Fd, DataPtr, WriteSize) != WriteSize) @@ -630,7 +630,7 @@ FileSystemTraversal::TraverseFileSystem(const std::filesystem::path& RootDir, Tr if (FAILED(hRes)) { - zen::ThrowSystemException(hRes, "Failed to open handle to volume root"); + ThrowSystemException(hRes, "Failed to open handle to volume root"); } while (Continue) 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 diff --git a/zencore/intmath.cpp b/zencore/intmath.cpp index 98c345c79..5a686dc8e 100644 --- a/zencore/intmath.cpp +++ b/zencore/intmath.cpp @@ -3,7 +3,7 @@ #include <zencore/endian.h> #include <zencore/intmath.h> -#include <doctest/doctest.h> +#include <zencore/testing.h> namespace zen { @@ -12,6 +12,8 @@ namespace zen { // Testing related code follows... // +#if ZEN_WITH_TESTS + void intmath_forcelink() { @@ -58,4 +60,6 @@ TEST_CASE("intmath") CHECK(ByteSwap(uint64_t(0x214d'6172'7469'6e21ull)) == 0x216e'6974'7261'4d21ull); } +#endif + } // namespace zen diff --git a/zencore/iobuffer.cpp b/zencore/iobuffer.cpp index beb969bc7..5d3458dba 100644 --- a/zencore/iobuffer.cpp +++ b/zencore/iobuffer.cpp @@ -2,14 +2,15 @@ #include <zencore/iobuffer.h> -#include <doctest/doctest.h> -#include <memory.h> #include <zencore/except.h> #include <zencore/filesystem.h> #include <zencore/fmtutils.h> #include <zencore/logging.h> #include <zencore/memory.h> +#include <zencore/testing.h> #include <zencore/thread.h> + +#include <memory.h> #include <system_error> #if ZEN_PLATFORM_WINDOWS @@ -456,6 +457,8 @@ IoBufferBuilder::MakeFromTemporaryFile(const path_char_t* FileName) ////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS + void iobuffer_forcelink() { @@ -468,4 +471,6 @@ TEST_CASE("IoBuffer") zen::IoBuffer buffer3(buffer2, 0, buffer2.Size()); } +#endif + } // namespace zen diff --git a/zencore/iohash.cpp b/zencore/iohash.cpp index ad8d89ff0..77076c133 100644 --- a/zencore/iohash.cpp +++ b/zencore/iohash.cpp @@ -5,8 +5,8 @@ #include <zencore/blake3.h> #include <zencore/compositebuffer.h> #include <zencore/string.h> +#include <zencore/testing.h> -#include <doctest/doctest.h> #include <gsl/gsl-lite.hpp> namespace zen { diff --git a/zencore/md5.cpp b/zencore/md5.cpp index 228c0feff..237f6cfdd 100644 --- a/zencore/md5.cpp +++ b/zencore/md5.cpp @@ -2,9 +2,9 @@ #include <zencore/md5.h> #include <zencore/string.h> +#include <zencore/testing.h> #include <zencore/zencore.h> -#include <doctest/doctest.h> #include <string.h> // big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN @@ -425,6 +425,8 @@ MD5::ToHexString(StringBuilderBase& outBuilder) const // Testing related code follows... // +#if ZEN_WITH_TESTS + void md5_forcelink() { @@ -443,4 +445,6 @@ TEST_CASE("MD5") { } +#endif + } // namespace zen diff --git a/zencore/memory.cpp b/zencore/memory.cpp index 26c8321e5..613b6ba67 100644 --- a/zencore/memory.cpp +++ b/zencore/memory.cpp @@ -2,6 +2,7 @@ #include <zencore/intmath.h> #include <zencore/memory.h> +#include <zencore/testing.h> #ifdef ZEN_PLATFORM_WINDOWS # include <malloc.h> @@ -9,8 +10,6 @@ # include <cstdlib> #endif -#include <doctest/doctest.h> - namespace zen { ////////////////////////////////////////////////////////////////////////// @@ -147,6 +146,8 @@ ChunkingLinearAllocator::Alloc(size_t Size, size_t Alignment) // Unit tests // +#if ZEN_WITH_TESTS + TEST_CASE("ChunkingLinearAllocator") { ChunkingLinearAllocator Allocator(4096); @@ -194,4 +195,6 @@ memory_forcelink() { } +#endif + } // namespace zen diff --git a/zencore/mpscqueue.cpp b/zencore/mpscqueue.cpp new file mode 100644 index 000000000..e1841ef63 --- /dev/null +++ b/zencore/mpscqueue.cpp @@ -0,0 +1,25 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include <zencore/mpscqueue.h> + +#include <zencore/testing.h> +#include <string> + +namespace zen { + +#if ZEN_WITH_TESTS && 0 +TEST_CASE("mpsc") +{ + MpscQueue<std::string> Queue; + Queue.Enqueue("hello"); + std::optional<std::string> Value = Queue.Dequeue(); + CHECK_EQ(Value, "hello"); +} +#endif + +void +mpscqueue_forcelink() +{ +} + +} // namespace zen
\ No newline at end of file diff --git a/zencore/refcount.cpp b/zencore/refcount.cpp index 943635552..33b530b90 100644 --- a/zencore/refcount.cpp +++ b/zencore/refcount.cpp @@ -2,7 +2,8 @@ #include <zencore/refcount.h> -#include <doctest/doctest.h> +#include <zencore/testing.h> + #include <functional> namespace zen { @@ -12,6 +13,8 @@ namespace zen { // Testing related code follows... // +#if ZEN_WITH_TESTS + struct TestRefClass : public RefCounted { ~TestRefClass() @@ -93,4 +96,6 @@ TEST_CASE("RefPtr on Stack allocated object") CHECK(IsDestroyed == true); } +#endif + } // namespace zen diff --git a/zencore/session.cpp b/zencore/session.cpp index d57d3685b..ce4bfae1b 100644 --- a/zencore/session.cpp +++ b/zencore/session.cpp @@ -9,14 +9,27 @@ namespace zen { static Oid GlobalSessionId; +static char GlobalSessionString[Oid::StringLength]; static std::once_flag SessionInitFlag; Oid GetSessionId() { - std::call_once(SessionInitFlag, [&] { GlobalSessionId.Generate(); }); + std::call_once(SessionInitFlag, [&] { + GlobalSessionId.Generate(); + GlobalSessionId.ToString(GlobalSessionString); + }); return GlobalSessionId; } +std::string_view +GetSessionIdString() +{ + // Ensure we actually have a generated session identifier + std::ignore = GetSessionId(); + + return std::string_view(GlobalSessionString, Oid::StringLength); +} + } // namespace zen
\ No newline at end of file diff --git a/zencore/sha1.cpp b/zencore/sha1.cpp index 3cc2f5cdf..8b4e7897f 100644 --- a/zencore/sha1.cpp +++ b/zencore/sha1.cpp @@ -6,9 +6,9 @@ #include <zencore/sha1.h> #include <zencore/string.h> +#include <zencore/testing.h> #include <zencore/zencore.h> -#include <doctest/doctest.h> #include <string.h> // big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN @@ -357,6 +357,8 @@ SHA1::ToHexString(StringBuilderBase& outBuilder) const // Testing related code follows... // +#if ZEN_WITH_TESTS + void sha1_forcelink() { @@ -436,4 +438,6 @@ TEST_CASE("SHA1") } } +#endif + } // namespace zen diff --git a/zencore/sharedbuffer.cpp b/zencore/sharedbuffer.cpp index 2761d0b4d..200e06972 100644 --- a/zencore/sharedbuffer.cpp +++ b/zencore/sharedbuffer.cpp @@ -2,7 +2,8 @@ #include <zencore/sharedbuffer.h> -#include <doctest/doctest.h> +#include <zencore/testing.h> + #include <memory.h> #include <gsl/gsl-lite.hpp> @@ -129,6 +130,8 @@ SharedBuffer::Clone(MemoryView View) ////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS + void sharedbuffer_forcelink() { @@ -138,4 +141,6 @@ TEST_CASE("SharedBuffer") { } +#endif + } // namespace zen diff --git a/zencore/snapshot_manifest.cpp b/zencore/snapshot_manifest.cpp deleted file mode 100644 index 87625fb7f..000000000 --- a/zencore/snapshot_manifest.cpp +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include <doctest/doctest.h> -#include <zencore/snapshot_manifest.h> -#include <zencore/stream.h> -#include <zencore/streamutil.h> -#include <zencore/string.h> -#include <ostream> - -#include <filesystem> - -#include <atlbase.h> - -// Used for getting My Documents for default snapshot dir -#include <ShlObj.h> -#if ZEN_PLATFORM_WINDOWS -# pragma comment(lib, "shell32.lib") -#endif - -namespace zen { - -constexpr const char* magicString = "-=- ZEN_SNAP -=-"; - -struct SerializedManifestHeader -{ - char Magic[16]; - - void init() { memcpy(Magic, magicString, sizeof Magic); } - bool verify() const { return memcmp(Magic, magicString, sizeof Magic) == 0; } -}; - -TextWriter& -operator<<(TextWriter& Writer, const LeafNode& Leaf) -{ - Writer << "modTime: " << Leaf.FileModifiedTime << ", size: " << Leaf.FileSize << ", hash: " << Leaf.ChunkHash << ", name: " << Leaf.Name - << "\n"; - - return Writer; -} - -BinaryWriter& -operator<<(BinaryWriter& Writer, const LeafNode& Leaf) -{ - Writer << Leaf.FileModifiedTime << Leaf.FileSize << Leaf.ChunkHash << Leaf.Name; - - return Writer; -} - -BinaryReader& -operator>>(BinaryReader& Reader, LeafNode& Leaf) -{ - Reader >> Leaf.FileModifiedTime >> Leaf.FileSize >> Leaf.ChunkHash >> Leaf.Name; - - return Reader; -} - -void -TreeNode::Finalize() -{ - zen::BLAKE3Stream Blake3Stream; - - for (auto& Node : Children) - { - Node.Finalize(); - Blake3Stream.Append(Node.ChunkHash.Hash, sizeof Node.ChunkHash); - Blake3Stream.Append(Node.Name.data(), Node.Name.size() + 1); - } - - for (auto& leaf : Leaves) - { - Blake3Stream.Append(leaf.ChunkHash.Hash, sizeof leaf.ChunkHash); - Blake3Stream.Append(leaf.Name.data(), leaf.Name.size() + 1); - } - - this->ChunkHash = Blake3Stream.GetHash(); -} - -void -TreeNode::VisitFiles(std::function<void(const LeafNode& node)> func) -{ - for (auto& Node : Children) - Node.VisitFiles(func); - - for (auto& Leaf : Leaves) - func(Leaf); -} - -void -TreeNode::VisitModifyFiles(std::function<void(LeafNode& node)> func) -{ - for (auto& Node : Children) - Node.VisitModifyFiles(func); - - for (auto& Leaf : Leaves) - func(Leaf); -} - -IndentTextWriter& -operator<<(IndentTextWriter& Writer, const TreeNode& Node) -{ - Writer << "hash: " << Node.ChunkHash << ", name: " << Node.Name << "\n"; - - if (!Node.Leaves.empty()) - { - Writer << "files: " - << "\n"; - - IndentTextWriter::Scope _(Writer); - - for (const LeafNode& Leaf : Node.Leaves) - Writer << Leaf; - } - - if (!Node.Children.empty()) - { - Writer << "children: " - << "\n"; - - IndentTextWriter::Scope _(Writer); - - for (const TreeNode& Child : Node.Children) - { - Writer << Child; - } - } - - return Writer; -} - -BinaryWriter& -operator<<(BinaryWriter& Writer, const TreeNode& Node) -{ - Writer << Node.ChunkHash << Node.Name; - Writer << uint32_t(Node.Children.size()); - - for (const TreeNode& child : Node.Children) - Writer << child; - - Writer << uint32_t(Node.Leaves.size()); - - for (const LeafNode& Leaf : Node.Leaves) - Writer << Leaf; - - return Writer; -} - -BinaryReader& -operator>>(BinaryReader& Reader, TreeNode& Node) -{ - Reader >> Node.ChunkHash >> Node.Name; - - uint32_t ChildCount = 0; - Reader >> ChildCount; - Node.Children.resize(ChildCount); - - for (TreeNode& Child : Node.Children) - Reader >> Child; - - uint32_t LeafCount = 0; - Reader >> LeafCount; - Node.Leaves.resize(LeafCount); - - for (LeafNode& Leaf : Node.Leaves) - Reader >> Leaf; - - return Reader; -} - -void -SnapshotManifest::finalize() -{ - Root.Finalize(); - - zen::BLAKE3Stream Blake3Stream; - - Blake3Stream.Append(Root.ChunkHash.Hash, sizeof Root.ChunkHash); - Blake3Stream.Append(Root.Name.data(), Root.Name.size() + 1); - - this->ChunkHash = Blake3Stream.GetHash(); -} - -void -WriteManifest(const SnapshotManifest& Manifest, OutStream& ToStream) -{ - BinaryWriter Out(ToStream); - SerializedManifestHeader Header; - Header.init(); - Out.Write(&Header, sizeof Header); - - Out << Manifest.ChunkHash << Manifest.Id << Manifest.Root; -} - -void -ReadManifest(SnapshotManifest& Manifest, InStream& FromStream) -{ - BinaryReader Reader(FromStream); - SerializedManifestHeader Header; - Reader.Read(&Header, sizeof Header); - - Reader >> Manifest.ChunkHash >> Manifest.Id >> Manifest.Root; -} - -void -PrintManifest(const SnapshotManifest& Manifest, OutStream& ToStream) -{ - IndentTextWriter Writer(ToStream); - - Writer << "hash: " << Manifest.ChunkHash << "\n"; - Writer << "id: " << Manifest.Id << "\n"; - Writer << "root: " - << "\n"; - IndentTextWriter::Scope _(Writer); - Writer << Manifest.Root; -} - -std::filesystem::path -ManifestSpecToPath(const char* ManifestSpec) -{ - ExtendableWideStringBuilder<128> ManifestTargetFile; - - if (ManifestSpec[0] == '#') - { - // Pick sensible default - - WCHAR MyDocumentsDir[MAX_PATH]; - HRESULT hRes = SHGetFolderPathW(NULL, - CSIDL_PERSONAL /* My Documents */, - NULL, - SHGFP_TYPE_CURRENT, - /* out */ MyDocumentsDir); - - if (SUCCEEDED(hRes)) - { - wcscat_s(MyDocumentsDir, L"\\zenfs\\Snapshots\\"); - - ManifestTargetFile.Append(MyDocumentsDir); - ManifestTargetFile.AppendAscii(ManifestSpec + 1); - } - } - else - { - ManifestTargetFile.AppendAscii(ManifestSpec); - } - - std::filesystem::path ManifestPath{ManifestTargetFile.c_str()}; - - if (ManifestPath.extension() != L".zenfs") - { - ManifestPath.append(L".zenfs"); - } - - return ManifestPath; -} - -////////////////////////////////////////////////////////////////////////// -// -// Testing related code follows... -// - -void -snapshotmanifest_forcelink() -{ -} - -TEST_CASE("Snapshot manifest") -{ - SnapshotManifest Manifest; - - Manifest.Id = "test_manifest"; - Manifest.ChunkHash = zen::BLAKE3::HashMemory("abcd", 4); - - MemoryOutStream Outstream; - WriteManifest(Manifest, Outstream); - - MemoryInStream Instream(Outstream.Data(), Outstream.Size()); - SnapshotManifest Manifest2; - ReadManifest(/* out */ Manifest2, Instream); - - CHECK(Manifest.Id == Manifest2.Id); - CHECK(Manifest.ChunkHash == Manifest2.ChunkHash); -} - -} // namespace zen diff --git a/zencore/stats.cpp b/zencore/stats.cpp index f8cdc8fbb..c5187940e 100644 --- a/zencore/stats.cpp +++ b/zencore/stats.cpp @@ -1,10 +1,13 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include "zencore/stats.h" -#include <doctest/doctest.h> #include <cmath> #include "zencore/timer.h" +#if ZEN_WITH_TESTS +# include <zencore/testing.h> +#endif + // // Derived from https://github.com/dln/medida/blob/master/src/medida/stats/ewma.cc // @@ -47,6 +50,8 @@ EWMA::Rate() const ////////////////////////////////////////////////////////////////////////// +#if ZEN_WITH_TESTS + TEST_CASE("Stats") { SUBCASE("Simple") @@ -70,4 +75,6 @@ stats_forcelink() { } +#endif + } // namespace zen diff --git a/zencore/stream.cpp b/zencore/stream.cpp index 8687d5501..ead0b014b 100644 --- a/zencore/stream.cpp +++ b/zencore/stream.cpp @@ -1,9 +1,10 @@ // Copyright Epic Games, Inc. All Rights Reserved. -#include <doctest/doctest.h> #include <stdarg.h> #include <zencore/memory.h> #include <zencore/stream.h> +#include <zencore/testing.h> + #include <algorithm> #include <stdexcept> @@ -279,6 +280,8 @@ IndentTextWriter::Write(const void* data, size_t byteCount) // Testing related code follows... // +#if ZEN_WITH_TESTS + void stream_forcelink() { @@ -336,4 +339,6 @@ TEST_CASE("BinaryWriter and BinaryWriter") CHECK(i64 == 42); } +#endif + } // namespace zen diff --git a/zencore/string.cpp b/zencore/string.cpp index 8ea10d2a3..6dcdc9542 100644 --- a/zencore/string.cpp +++ b/zencore/string.cpp @@ -1,11 +1,12 @@ // Copyright Epic Games, Inc. All Rights Reserved. -#include <doctest/doctest.h> +#include <zencore/memory.h> +#include <zencore/string.h> +#include <zencore/testing.h> + #include <inttypes.h> #include <math.h> #include <stdio.h> -#include <zencore/memory.h> -#include <zencore/string.h> #include <exception> #include <ostream> #include <stdexcept> @@ -452,6 +453,8 @@ template class StringBuilderImpl<wchar_t>; // Unit tests // +#if ZEN_WITH_TESTS + TEST_CASE("niceNum") { char Buffer[16]; @@ -912,4 +915,46 @@ TEST_CASE("filepath") CHECK(FilepathFindExtension(".txt") == std::string_view(".txt")); } +TEST_CASE("string") +{ + using namespace std::literals; + + SUBCASE("ForEachStrTok") + { + const auto Tokens = "here,is,my,different,tokens"sv; + int32_t ExpectedTokenCount = 5; + int32_t TokenCount = 0; + StringBuilder<512> Sb; + + TokenCount = ForEachStrTok(Tokens, ',', [&Sb](const std::string_view& Token) { + if (Sb.Size()) + { + Sb << ","; + } + Sb << Token; + return true; + }); + + CHECK(TokenCount == ExpectedTokenCount); + CHECK(Sb.ToString() == Tokens); + + ExpectedTokenCount = 1; + const auto Str = "mosdef"sv; + + Sb.Reset(); + TokenCount = ForEachStrTok(Str, ' ', [&Sb](const std::string_view& Token) { + Sb << Token; + return true; + }); + CHECK(Sb.ToString() == Str); + CHECK(TokenCount == ExpectedTokenCount); + + ExpectedTokenCount = 0; + TokenCount = ForEachStrTok(""sv, ',', [](const std::string_view&) { return true; }); + CHECK(TokenCount == ExpectedTokenCount); + } +} + +#endif + } // namespace zen diff --git a/zencore/testutils.cpp b/zencore/testutils.cpp new file mode 100644 index 000000000..116491950 --- /dev/null +++ b/zencore/testutils.cpp @@ -0,0 +1,33 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zencore/testutils.h" +#include <zencore/session.h> +#include "zencore/string.h" + +namespace zen { + +static std::atomic<int> Sequence{0}; + +std::filesystem::path +CreateTemporaryDirectory() +{ + std::error_code Ec; + + std::filesystem::path DirPath = std::filesystem::temp_directory_path() / GetSessionIdString() / IntNum(++Sequence).c_str(); + std::filesystem::remove_all(DirPath, Ec); + std::filesystem::create_directories(DirPath); + + return DirPath; +} + +ScopedTemporaryDirectory::ScopedTemporaryDirectory() : m_RootPath(CreateTemporaryDirectory()) +{ +} + +ScopedTemporaryDirectory::~ScopedTemporaryDirectory() +{ + std::error_code Ec; + std::filesystem::remove_all(m_RootPath, Ec); +} + +} // namespace zen
\ No newline at end of file diff --git a/zencore/thread.cpp b/zencore/thread.cpp index 620ea3bff..ded180337 100644 --- a/zencore/thread.cpp +++ b/zencore/thread.cpp @@ -17,41 +17,25 @@ namespace zen { void RwLock::AcquireShared() { -#if ZEN_PLATFORM_WINDOWS - AcquireSRWLockShared((PSRWLOCK)&m_Srw); -#else m_Mutex.lock_shared(); -#endif } void RwLock::ReleaseShared() { -#if ZEN_PLATFORM_WINDOWS - ReleaseSRWLockShared((PSRWLOCK)&m_Srw); -#else m_Mutex.unlock_shared(); -#endif } void RwLock::AcquireExclusive() { -#if ZEN_PLATFORM_WINDOWS - AcquireSRWLockExclusive((PSRWLOCK)&m_Srw); -#else m_Mutex.lock(); -#endif } void RwLock::ReleaseExclusive() { -#if ZEN_PLATFORM_WINDOWS - ReleaseSRWLockExclusive((PSRWLOCK)&m_Srw); -#else m_Mutex.unlock(); -#endif } ////////////////////////////////////////////////////////////////////////// @@ -78,6 +62,13 @@ Event::Reset() ResetEvent(m_EventHandle); } +void +Event::Close() +{ + CloseHandle(m_EventHandle); + m_EventHandle = nullptr; +} + bool Event::Wait(int TimeoutMs) { @@ -174,6 +165,7 @@ ProcessHandle::Initialize(void* ProcessHandle) ZEN_ASSERT(m_ProcessHandle == nullptr); // TODO: perform some debug verification here to verify it's a valid handle? m_ProcessHandle = ProcessHandle; + m_Pid = GetProcessId(m_ProcessHandle); } ProcessHandle::~ProcessHandle() @@ -255,7 +247,8 @@ ProcessHandle::Wait(int TimeoutMs) case WAIT_FAILED: // What might go wrong here, and what is meaningful to act on? - throw WindowsException("Process::Wait failed"); + using namespace std::literals; + ThrowLastError("Process::Wait failed"sv); } return false; @@ -263,19 +256,100 @@ ProcessHandle::Wait(int TimeoutMs) ////////////////////////////////////////////////////////////////////////// +ProcessMonitor::ProcessMonitor() +{ +} + +ProcessMonitor::~ProcessMonitor() +{ + RwLock::ExclusiveLockScope _(m_Lock); + + for (HANDLE& Proc : m_ProcessHandles) + { + CloseHandle(Proc); + Proc = 0; + } +} + +bool +ProcessMonitor::IsRunning() +{ + RwLock::ExclusiveLockScope _(m_Lock); + + bool FoundOne = false; + + for (HANDLE& Proc : m_ProcessHandles) + { + DWORD ExitCode = 0; + GetExitCodeProcess(Proc, &ExitCode); + + if (ExitCode != STILL_ACTIVE) + { + CloseHandle(Proc); + Proc = 0; + } + else + { + // Still alive + FoundOne = true; + } + } + + std::erase_if(m_ProcessHandles, [](HANDLE Handle) { return Handle == 0; }); + + return FoundOne; +} + +void +ProcessMonitor::AddPid(int Pid) +{ + HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Pid); + + if (ProcessHandle) + { + RwLock::ExclusiveLockScope _(m_Lock); + m_ProcessHandles.push_back(ProcessHandle); + } +} + +bool +ProcessMonitor::IsActive() const +{ + RwLock::SharedLockScope _(m_Lock); + return m_ProcessHandles.empty() == false; +} + +////////////////////////////////////////////////////////////////////////// + bool IsProcessRunning(int pid) { + // This function is arguably not super useful, a pid can be re-used + // by the OS so holding on to a pid and polling it over some time + // period will not necessarily tell you what you probably want to know. + +#if ZEN_PLATFORM_WINDOWS HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); - if (hProc == NULL) + if (!hProc) { - return false; + DWORD Error = zen::GetLastError(); + + if (Error == ERROR_INVALID_PARAMETER) + { + return false; + } + + using namespace fmt::literals; + ThrowSystemError(Error, "failed to open process with pid {}"_format(pid)); } CloseHandle(hProc); return true; +#else + ZEN_NOT_IMPLEMENTED(); +#endif } int diff --git a/zencore/timer.cpp b/zencore/timer.cpp index 08b5e06d2..1e73a7532 100644 --- a/zencore/timer.cpp +++ b/zencore/timer.cpp @@ -1,8 +1,10 @@ // Copyright Epic Games, Inc. All Rights Reserved. -#include <doctest/doctest.h> #include <zencore/thread.h> #include <zencore/timer.h> + +#include <zencore/testing.h> + #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> #elif ZEN_PLATFORM_LINUX @@ -62,6 +64,8 @@ GetHifreqTimerFrequencySafe() // Testing related code follows... // +#if ZEN_WITH_TESTS + void timer_forcelink() { @@ -79,4 +83,6 @@ TEST_CASE("Timer") CHECK_NE(s0, s1); } +#endif + } // namespace zen diff --git a/zencore/uid.cpp b/zencore/uid.cpp index acf9f9790..d4b708288 100644 --- a/zencore/uid.cpp +++ b/zencore/uid.cpp @@ -4,6 +4,7 @@ #include <zencore/endian.h> #include <zencore/string.h> +#include <zencore/testing.h> #include <atomic> #include <bit> @@ -12,8 +13,6 @@ #include <set> #include <unordered_map> -#include <doctest/doctest.h> - namespace zen { ////////////////////////////////////////////////////////////////////////// @@ -83,10 +82,24 @@ Oid::FromHexString(const std::string_view String) } } +Oid +Oid::FromMemory(const void* Ptr) +{ + Oid Id; + memcpy(Id.OidBits, Ptr, sizeof Id); + return Id; +} + +void +Oid::ToString(char OutString[StringLength]) +{ + ToHexBytes(reinterpret_cast<const uint8_t*>(OidBits), sizeof(Oid::OidBits), OutString); +} + StringBuilderBase& Oid::ToString(StringBuilderBase& OutString) const { - char str[25]; + char str[StringLength + 1]; ToHexBytes(reinterpret_cast<const uint8_t*>(OidBits), sizeof(Oid::OidBits), str); str[2 * sizeof(Oid)] = '\0'; @@ -95,6 +108,8 @@ Oid::ToString(StringBuilderBase& OutString) const return OutString; } +#if ZEN_WITH_TESTS + TEST_CASE("Oid") { SUBCASE("Basic") @@ -129,4 +144,6 @@ uid_forcelink() { } +#endif + } // namespace zen diff --git a/zencore/xxhash.cpp b/zencore/xxhash.cpp index a20ee10bd..450131d19 100644 --- a/zencore/xxhash.cpp +++ b/zencore/xxhash.cpp @@ -3,8 +3,8 @@ #include <zencore/xxhash.h> #include <zencore/string.h> +#include <zencore/testing.h> -#include <doctest/doctest.h> #include <gsl/gsl-lite.hpp> namespace zen { diff --git a/zencore/zencore.cpp b/zencore/zencore.cpp index f9b19ba9d..3eb43c558 100644 --- a/zencore/zencore.cpp +++ b/zencore/zencore.cpp @@ -20,9 +20,9 @@ #include <zencore/intmath.h> #include <zencore/iobuffer.h> #include <zencore/memory.h> +#include <zencore/mpscqueue.h> #include <zencore/refcount.h> #include <zencore/sha1.h> -#include <zencore/snapshot_manifest.h> #include <zencore/stats.h> #include <zencore/stream.h> #include <zencore/string.h> @@ -30,6 +30,10 @@ #include <zencore/timer.h> #include <zencore/uid.h> +namespace zen { + +////////////////////////////////////////////////////////////////////////// + bool IsPointerToStack(const void* ptr) { @@ -55,12 +59,31 @@ IsPointerToStack(const void* ptr) #endif } -zen::AssertException::AssertException(const char* Msg) : m_Msg(Msg) +bool +IsDebuggerPresent() { +#if ZEN_PLATFORM_WINDOWS + return ::IsDebuggerPresent(); +#else + return false; +#endif } -zen::AssertException::~AssertException() +bool +IsInteractiveSession() { +#if ZEN_PLATFORM_WINDOWS + DWORD dwSessionId = 0; + if (ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId)) + { + return (dwSessionId != 0); + } + + return false; +#else + // TODO: figure out what makes sense here + return true; +#endif } ////////////////////////////////////////////////////////////////////////// @@ -81,6 +104,7 @@ RequestApplicationExit(int ExitCode) s_ApplicationExitRequested = true; } +#if ZEN_WITH_TESTS void zencore_forcelinktests() { @@ -91,9 +115,9 @@ zencore_forcelinktests() zen::intmath_forcelink(); zen::iobuffer_forcelink(); zen::memory_forcelink(); + zen::mpscqueue_forcelink(); zen::refcount_forcelink(); zen::sha1_forcelink(); - zen::snapshotmanifest_forcelink(); zen::stats_forcelink(); zen::stream_forcelink(); zen::string_forcelink(); @@ -104,3 +128,6 @@ zencore_forcelinktests() zen::usonbuilder_forcelink(); zen::usonpackage_forcelink(); } +#endif + +} // namespace zen diff --git a/zencore/zencore.vcxproj b/zencore/zencore.vcxproj index 150c42cd6..2322f7173 100644 --- a/zencore/zencore.vcxproj +++ b/zencore/zencore.vcxproj @@ -128,6 +128,7 @@ <ClInclude Include="include\zencore\md5.h" /> <ClInclude Include="include\zencore\memory.h" /> <ClInclude Include="include\zencore\meta.h" /> + <ClInclude Include="include\zencore\mpscqueue.h" /> <ClInclude Include="include\zencore\postwindows.h" /> <ClInclude Include="include\zencore\prewindows.h" /> <ClInclude Include="include\zencore\refcount.h" /> @@ -136,12 +137,13 @@ <ClInclude Include="include\zencore\sha1.h" /> <ClInclude Include="include\zencore\iobuffer.h" /> <ClInclude Include="include\zencore\sharedbuffer.h" /> - <ClInclude Include="include\zencore\snapshot_manifest.h" /> <ClInclude Include="include\zencore\stats.h" /> <ClInclude Include="include\zencore\stream.h" /> <ClInclude Include="include\zencore\streamutil.h" /> <ClInclude Include="include\zencore\string.h" /> <ClInclude Include="include\zencore\targetver.h" /> + <ClInclude Include="include\zencore\testing.h" /> + <ClInclude Include="include\zencore\testutils.h" /> <ClInclude Include="include\zencore\thread.h" /> <ClInclude Include="include\zencore\timer.h" /> <ClInclude Include="include\zencore\uid.h" /> @@ -166,6 +168,7 @@ <ClCompile Include="logging.cpp" /> <ClCompile Include="md5.cpp" /> <ClCompile Include="memory.cpp" /> + <ClCompile Include="mpscqueue.cpp" /> <ClCompile Include="refcount.cpp" /> <ClCompile Include="session.cpp" /> <ClCompile Include="sha1.cpp"> @@ -176,11 +179,11 @@ </ClCompile> <ClCompile Include="iobuffer.cpp" /> <ClCompile Include="sharedbuffer.cpp" /> - <ClCompile Include="snapshot_manifest.cpp" /> <ClCompile Include="stats.cpp" /> <ClCompile Include="stream.cpp" /> <ClCompile Include="streamutil.cpp" /> <ClCompile Include="string.cpp" /> + <ClCompile Include="testutils.cpp" /> <ClCompile Include="thread.cpp" /> <ClCompile Include="timer.cpp" /> <ClCompile Include="uid.cpp" /> diff --git a/zencore/zencore.vcxproj.filters b/zencore/zencore.vcxproj.filters index ea0f8a912..d2e7a3159 100644 --- a/zencore/zencore.vcxproj.filters +++ b/zencore/zencore.vcxproj.filters @@ -4,7 +4,6 @@ <ClInclude Include="include\zencore\intmath.h" /> <ClInclude Include="include\zencore\scopeguard.h" /> <ClInclude Include="include\zencore\sha1.h" /> - <ClInclude Include="include\zencore\snapshot_manifest.h" /> <ClInclude Include="include\zencore\targetver.h" /> <ClInclude Include="include\zencore\zencore.h" /> <ClInclude Include="include\zencore\compactbinary.h" /> @@ -42,9 +41,11 @@ <ClInclude Include="include\zencore\postwindows.h" /> <ClInclude Include="include\zencore\logging.h" /> <ClInclude Include="include\zencore\session.h" /> + <ClInclude Include="include\zencore\testutils.h" /> + <ClInclude Include="include\zencore\testing.h" /> + <ClInclude Include="include\zencore\mpscqueue.h" /> </ItemGroup> <ItemGroup> - <ClCompile Include="snapshot_manifest.cpp" /> <ClCompile Include="sha1.cpp" /> <ClCompile Include="zencore.cpp" /> <ClCompile Include="compactbinary.cpp" /> @@ -74,6 +75,8 @@ <ClCompile Include="logging.cpp" /> <ClCompile Include="intmath.cpp" /> <ClCompile Include="session.cpp" /> + <ClCompile Include="testutils.cpp" /> + <ClCompile Include="mpscqueue.cpp" /> </ItemGroup> <ItemGroup> <Filter Include="CAS"> |